diff options
Diffstat (limited to 'sca-cpp')
37 files changed, 1575 insertions, 912 deletions
diff --git a/sca-cpp/trunk/kernel/dynlib-test.cpp b/sca-cpp/trunk/kernel/dynlib-test.cpp new file mode 100644 index 0000000000..0123b5ec79 --- /dev/null +++ b/sca-cpp/trunk/kernel/dynlib-test.cpp @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 library. + */ + +#include "function.hpp" + +namespace tuscany { +namespace test { + + const int cppsquare(int x) { + return x * x; + } + +} +} + +extern "C" { + + const int csquare(const int x) { + return tuscany::test::cppsquare(x); + } + + const tuscany::lambda<int(int)> csquarel(const int x) { + return tuscany::lambda<int(int)>(tuscany::test::cppsquare); + } + +} diff --git a/sca-cpp/trunk/kernel/dynlib.hpp b/sca-cpp/trunk/kernel/dynlib.hpp new file mode 100644 index 0000000000..10a5a030cb --- /dev/null +++ b/sca-cpp/trunk/kernel/dynlib.hpp @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_dlib_hpp +#define tuscany_dlib_hpp + +/** + * Simple dynamic library access functions. + */ + +#include <dlfcn.h> + +#include "function.hpp" +#include "gc.hpp" +#include "monad.hpp" + +namespace tuscany { + +/** + * OS specific dynamic library file extension. + */ +#ifdef IS_DARWIN +const std::string dynlibExt(".dylib"); +#else +const std::string dynlibExt(".so"); +#endif + +/** + * Represents a reference to a dynamic library. + */ +class lib { +public: + lib() : dl(NULL) { + } + + lib(const std::string& name) : dl(new DynLib(name)) { + } + + ~lib() { + } + +private: + class DynLib { + public: + DynLib(const std::string& name) : name(name), h(dlopen(name.c_str(), RTLD_NOW)) { + } + ~DynLib() { + if (h == NULL) + return; + dlclose(h); + } + + const std::string name; + void* h; + }; + + gc_ptr<DynLib> dl; + + friend const failable<lib, std::string> dynlib(const std::string& name); + template<typename S> friend const failable<lambda<S>, std::string> dynlambda(const std::string& name, const lib& l); +}; + +/** + * Load a dynamic library. + */ +const failable<lib, std::string> dynlib(const std::string& name) { + const lib l(name); + if (l.dl->h == NULL) + return mkfailure<lib, std::string>("Could not load library: " + name + ": " + dlerror()); + return l; +} + +/** + * Find a lambda function in a dynamic library. + */ +template<typename S> const failable<lambda<S>, std::string> dynlambda(const std::string& name, const lib& l) { + const void* s = dlsym(l.dl->h, name.c_str()); + if (s == NULL) + return mkfailure<lambda<S>, std::string>(std::string("Could not load symbol: " + name)); + return lambda<S>((S*)s); +} + +} +#endif /* tuscany_dlib_hpp */ diff --git a/sca-cpp/trunk/kernel/kernel-test.cpp b/sca-cpp/trunk/kernel/kernel-test.cpp index 9346e31b71..24d67e1a9a 100644 --- a/sca-cpp/trunk/kernel/kernel-test.cpp +++ b/sca-cpp/trunk/kernel/kernel-test.cpp @@ -37,6 +37,7 @@ #include "element.hpp" #include "xml.hpp" #include "monad.hpp" +#include "dynlib.hpp" namespace tuscany { @@ -660,8 +661,8 @@ struct tickInc { const double v; tickInc(const double v) : v(v) { } - const svp<int, double> operator()(int s) const { - return svp<int, double>(s + 1, v); + const scp<int, double> operator()(int s) const { + return scp<int, double>(s + 1, v); } }; @@ -693,6 +694,21 @@ bool testStateMonad() { return true; } +bool testDynLib() { + const failable<lib, std::string> dl(dynlib(".libs/libdynlib-test" + dynlibExt)); + assert(hasContent(dl)); + const failable<lambda<int(int)>, std::string> sq(dynlambda<int(int)>("csquare", content(dl))); + assert(hasContent(sq)); + lambda<int(int)> l(content(sq)); + assert(l(2) == 4); + + const failable<lambda<lambda<int(int)>()>, std::string> sql(dynlambda<lambda<int(int)>()>("csquarel", content(dl))); + assert(hasContent(sql)); + lambda<lambda<int(int)>()> ll(content(sql)); + assert(ll()(3) == 9); + return true; +} + } int main() { @@ -730,6 +746,7 @@ int main() { tuscany::testMaybeMonad(); tuscany::testFailableMonad(); tuscany::testStateMonad(); + tuscany::testDynLib(); std::cout << "OK" << std::endl; diff --git a/sca-cpp/trunk/kernel/monad.hpp b/sca-cpp/trunk/kernel/monad.hpp index 98eb3799c0..1a45640c32 100644 --- a/sca-cpp/trunk/kernel/monad.hpp +++ b/sca-cpp/trunk/kernel/monad.hpp @@ -42,10 +42,6 @@ public: id(const V& v) : v(v) { } - operator const V() const { - return v; - } - const id<V>& operator=(const id<V>& m) { if(this == &m) return *this; @@ -65,17 +61,26 @@ public: private: const V v; + + template<typename X> friend const X content(const id<X>& m); }; /** * Write an identity monad to a stream. */ template<typename V> std::ostream& operator<<(std::ostream& out, const id<V>& m) { - out << (V)m; + out << content(m); return out; } /** + * Returns the content of an identity monad. + */ +template<typename V> const V content(const id<V>& m) { + return m.v; +} + +/** * Return an identity monad from a value. */ template<typename V> const id<V> mkunit(const V& v) { @@ -90,11 +95,11 @@ template<typename V> const lambda<id<V>(V)> unit() { * Bind a function to an identity monad. Pass the value in the monad to the function. */ template<typename R, typename V> const id<R> operator>>(const id<V>& m, const lambda<id<R>(V)>& f) { - return f(m); + return f(content(m)); } template<typename R, typename V> const id<R> operator>>(const id<V>& m, const id<R> (* const f)(const V)) { - return f(m); + return f(content(m)); } /** @@ -109,10 +114,6 @@ public: maybe() : hasv(false) { } - operator const V() const { - return v; - } - const maybe<V>& operator=(const maybe<V>& m) { if(this == &m) return *this; @@ -138,18 +139,19 @@ private: const bool hasv; V v; - template<typename A> friend const bool hasValue(const maybe<A>& m); + template<typename X> friend const bool hasContent(const maybe<X>& m); + template<typename X> friend const X content(const maybe<X>& m); }; /** * Write a maybe monad to a stream. */ template<typename V> std::ostream& operator<<(std::ostream& out, const maybe<V>& m) { - if (!hasValue(m)) { + if (!hasContent(m)) { out << "nothing"; return out; } - out << (V)m; + out << content(m); return out; } @@ -165,26 +167,33 @@ template<typename V> const lambda<maybe<V>(V)> just() { } /** - * Returns true if the monad contains a value. + * Returns true if a maybe monad contains a content. */ -template<typename V> const bool hasValue(const maybe<V>& m) { +template<typename V> const bool hasContent(const maybe<V>& m) { return m.hasv; } /** + * Returns the content of a maybe monad. + */ +template<typename V> const V content(const maybe<V>& m) { + return m.v; +} + +/** * Bind a function to a maybe monad. Passes the value in the monad to the function * if present, or does nothing if there's no value. */ template<typename R, typename V> const maybe<R> operator>>(const maybe<V>& m, const lambda<maybe<R>(V)>& f) { - if (!hasValue(m)) + if (!hasContent(m)) return m; - return f(m); + return f(content(m)); } template<typename R, typename V> const maybe<R> operator>>(const maybe<V>& m, const maybe<R> (* const f)(const V)) { - if (!hasValue(m)) + if (!hasContent(m)) return m; - return f(m); + return f(content(m)); } /** @@ -207,10 +216,6 @@ public: f = m.f; } - operator const V() const { - return v; - } - const failable<V, F>& operator=(const failable<V, F>& m) { if(this == &m) return *this; @@ -242,7 +247,8 @@ private: failable(const bool hasv, const F& f) : hasv(hasv), f(f) { } - template<typename A, typename B> friend const bool hasValue(const failable<A, B>& m); + template<typename A, typename B> friend const bool hasContent(const failable<A, B>& m); + template<typename A, typename B> friend const A content(const failable<A, B>& m); template<typename A, typename B> friend const B reason(const failable<A, B>& m); template<typename A, typename B> friend const failable<A, B> mkfailure(const B& f); }; @@ -251,12 +257,11 @@ private: * Write a failable monad to a stream. */ template<typename V, typename F> std::ostream& operator<<(std::ostream& out, const failable<V, F>& m) { - if (!hasValue(m)) { + if (!hasContent(m)) { out << reason(m); return out; } - const V v = m; - out << v; + out << content(m); return out; } @@ -283,13 +288,20 @@ template<typename V, typename F> const lambda<failable<V, F>(V)> failure() { } /** - * Returns true if the monad contains a value. + * Returns true if the monad contains a content. */ -template<typename V, typename F> const bool hasValue(const failable<V, F>& m) { +template<typename V, typename F> const bool hasContent(const failable<V, F>& m) { return m.hasv; } /** + * Returns the content of a failable monad. + */ +template<typename V, typename F> const V content(const failable<V, F>& m) { + return m.v; +} + +/** * Returns the reason for failure of a failable monad. */ template<typename V, typename F> const F reason(const failable<V, F>& m) { @@ -302,24 +314,24 @@ template<typename V, typename F> const F reason(const failable<V, F>& m) { */ template<typename R, typename FR, typename V, typename FV> const failable<R, FR> operator>>(const failable<V, FV>& m, const lambda<failable<R, FR>(V)>& f) { - if (!hasValue(m)) + if (!hasContent(m)) return m; - return f(m); + return f(content(m)); } template<typename R, typename FR, typename V, typename FV> const failable<R, FR> operator>>(const failable<V, FV>& m, const failable<R, FR> (* const f)(const V)) { - if (!hasValue(m)) + if (!hasContent(m)) return m; - return f(m); + return f(content(m)); } /** - * State + value pair data type used by the state monad. + * State + content pair data type used by the state monad. */ -template<typename S, typename V> class svp { +template<typename S, typename V> class scp { public: - svp(const S& s, const V& v) : s(s), v(v) { + scp(const S& s, const V& v) : s(s), v(v) { } operator const S() const { @@ -330,7 +342,7 @@ public: return v; } - const svp<S, V>& operator=(const svp<S, V>& p) { + const scp<S, V>& operator=(const scp<S, V>& p) { if(this == &p) return *this; s = p.s; @@ -338,11 +350,11 @@ public: return *this; } - const bool operator!=(const svp<S, V>& p) const { + const bool operator!=(const scp<S, V>& p) const { return !this->operator==(p); } - const bool operator==(const svp<S, V>& p) const { + const bool operator==(const scp<S, V>& p) const { if (this == &p) return true; return s == p.s && v == p.v; @@ -351,19 +363,34 @@ public: private: const S s; const V v; + + template<typename A, typename B> friend const A scpstate(const scp<A, B>& m); + template<typename A, typename B> friend const B content(const scp<A, B>& m); }; /** - * State monad. Used to represent the combination of a state and a value. - * To get the state in the monad, just cast it to the state type. - * To get the value in the monad, just cast it to the value type. + * Returns the state of a state-content pair. + */ +template<typename S, typename V> const S scpstate(const scp<S, V>& m) { + return m.s; +} + +/** + * Returns the content of a state-content pair. + */ +template<typename S, typename V> const S content(const scp<S, V>& m) { + return m.v; +} + +/** + * State monad. Used to represent the combination of a state and a content. */ template<typename S, typename V> class state { public: - state(const lambda<svp<S, V>(S)>& f) : f(f) { + state(const lambda<scp<S, V>(S)>& f) : f(f) { } - const svp<S, V> operator()(const S& s) const { + const scp<S, V> operator()(const S& s) const { return f(s); } @@ -385,7 +412,7 @@ public: } private: - const lambda<svp<S, V>(S)> f; + const lambda<scp<S, V>(S)> f; }; /** @@ -399,14 +426,14 @@ template<typename S, typename V> std::ostream& operator<<(std::ostream& out, con } /** - * Return a state monad carrying a result value. + * Return a state monad carrying a result content. */ template<typename S, typename V> struct returnState { const V v; returnState(const V& v) : v(v) { } - const svp<S, V> operator()(const S& s) const { - return svp<S, V>(s, v); + const scp<S, V> operator()(const S& s) const { + return scp<S, V>(s, v); } }; @@ -416,16 +443,16 @@ template<typename S, typename V> const state<S, V> result(const V& v) { /** * Return a state monad with a transformer function. - * A transformer function takes a state and returns an svp pair carrying a value and a + * A transformer function takes a state and returns an scp pair carrying a content and a * new (transformed) state. */ -template<typename S, typename V> const state<S, V> transformer(const lambda<svp<S, V>(S)>& f) { +template<typename S, typename V> const state<S, V> transformer(const lambda<scp<S, V>(S)>& f) { return state<S, V>(f); } /** - * Bind a function to a state monad. The function takes a value and returns a state - * monad carrying a return value. + * Bind a function to a state monad. The function takes a content and returns a state + * monad carrying a return content. */ template<typename S, typename A, typename B> struct stateBind { const state<S, A> st; @@ -434,8 +461,8 @@ template<typename S, typename A, typename B> struct stateBind { stateBind(const state<S, A>& st, const lambda<state<S, B>(A)>& f) : st(st), f(f) { } - const svp<S, B> operator()(const S& is) const { - const svp<S, A> iscp = st(is); + const scp<S, B> operator()(const S& is) const { + const scp<S, A> iscp = st(is); const state<S, B> m = f((A)iscp); return m((S)iscp); } diff --git a/sca-cpp/trunk/kernel/xml.hpp b/sca-cpp/trunk/kernel/xml.hpp index 8d561557ca..4b4bcd098e 100644 --- a/sca-cpp/trunk/kernel/xml.hpp +++ b/sca-cpp/trunk/kernel/xml.hpp @@ -261,7 +261,7 @@ const failable<bool, std::string> writeList(const list<value>& l, const xmlTextW // Write its children const failable<bool, std::string> w = writeList(elementChildren(token), xml); - if (!hasValue(w)) + if (!hasContent(w)) return w; if (xmlTextWriterEndElement(xml) < 0) @@ -276,7 +276,7 @@ const failable<bool, std::string> writeList(const list<value>& l, const xmlTextW // Write its children const failable<bool, std::string> w = writeList(elementChildren(token), xml); - if (!hasValue(w)) + if (!hasContent(w)) return w; if (xmlTextWriterEndElement(xml) < 0) @@ -302,7 +302,7 @@ const failable<bool, std::string> write(const list<value>& l, const xmlTextWrite return mkfailure<bool, std::string>("xmlTextWriterStartDocument failed"); const failable<bool, std::string> w = writeList(l, xml); - if (!hasValue(w)) + if (!hasContent(w)) return w; if (xmlTextWriterEndDocument(xml) < 0) @@ -344,7 +344,7 @@ template<typename R> const failable<R, std::string> writeXML(const lambda<R(std: const failable<bool, std::string> w = write(l, xml); xmlFreeTextWriter(xml); - if (!hasValue(w)) { + if (!hasContent(w)) { return mkfailure<R, std::string>(reason(w)); } return cx.accum; @@ -355,9 +355,9 @@ template<typename R> const failable<R, std::string> writeXML(const lambda<R(std: */ const failable<list<std::string>, std::string> writeXML(const list<value>& l) { const failable<list<std::string>, std::string> ls = writeXML<list<std::string> >(rcons<std::string>, list<std::string>(), l); - if (!hasValue(ls)) + if (!hasContent(ls)) return ls; - return reverse(list<std::string>(ls)); + return reverse(list<std::string>(content(ls))); } } diff --git a/sca-cpp/trunk/modules/atom/atom-test.cpp b/sca-cpp/trunk/modules/atom/atom-test.cpp index 7c14b954a0..ba651e8eb2 100644 --- a/sca-cpp/trunk/modules/atom/atom-test.cpp +++ b/sca-cpp/trunk/modules/atom/atom-test.cpp @@ -82,13 +82,13 @@ bool testEntry() { assert(os.str() == itemEntry); } { - const list<value> a = readEntry(mklist(itemEntry)); + const list<value> a = content(readEntry(mklist(itemEntry))); std::ostringstream os; writeATOMEntry<std::ostringstream*>(writer, &os, a); assert(os.str() == itemEntry); } { - const list<value> a = readEntry(mklist(incompleteEntry)); + const list<value> a = content(readEntry(mklist(incompleteEntry))); std::ostringstream os; writeATOMEntry<std::ostringstream*>(writer, &os, a); assert(os.str() == completedEntry); @@ -135,7 +135,7 @@ bool testFeed() { assert(os.str() == emptyFeed); } { - const list<value> a = readFeed(mklist(emptyFeed)); + const list<value> a = content(readFeed(mklist(emptyFeed))); std::ostringstream os; writeATOMFeed<std::ostringstream*>(writer, &os, a); assert(os.str() == emptyFeed); @@ -171,7 +171,7 @@ bool testFeed() { assert(os.str() == itemFeed); } { - const list<value> a = readFeed(mklist(itemFeed)); + const list<value> a = content(readFeed(mklist(itemFeed))); std::ostringstream os; writeATOMFeed<std::ostringstream*>(writer, &os, a); assert(os.str() == itemFeed); diff --git a/sca-cpp/trunk/modules/atom/atom.hpp b/sca-cpp/trunk/modules/atom/atom.hpp index 5054c635a0..ac19b93e38 100644 --- a/sca-cpp/trunk/modules/atom/atom.hpp +++ b/sca-cpp/trunk/modules/atom/atom.hpp @@ -113,9 +113,9 @@ template<typename R> const failable<R, std::string> writeATOMEntry(const lambda< 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)) + if (!hasContent(ls)) return ls; - return reverse(list<std::string>(ls)); + return reverse(list<std::string>(content(ls))); } /** @@ -139,9 +139,9 @@ template<typename R> const failable<R, std::string> writeATOMFeed(const lambda<R */ 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)) + if (!hasContent(ls)) return ls; - return reverse(list<std::string>(ls)); + return reverse(list<std::string>(content(ls))); } /** diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp index 863aa98828..0a6fbcd8a6 100644 --- a/sca-cpp/trunk/modules/http/curl-test.cpp +++ b/sca-cpp/trunk/modules/http/curl-test.cpp @@ -53,16 +53,15 @@ 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)); + const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8090", ch); + assert(hasContent(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")); + const failable<value, std::string> r = get("http://localhost:8090", ch); + assert(hasContent(r)); + assert(contains(content(r), "It works")); } return true; } @@ -70,10 +69,9 @@ const bool testGet() { 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")); + const failable<value, std::string> r = get("http://localhost:8090", ch); + assert(hasContent(r)); + assert(contains(content(r), "It works")); return testGetLoop(count - 1, ch); } @@ -95,142 +93,6 @@ const bool testGetPerf() { 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; -} - } } @@ -239,13 +101,6 @@ int main() { 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; diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp index 5ee3a090b0..6c3a3a47dc 100644 --- a/sca-cpp/trunk/modules/http/curl.hpp +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -209,29 +209,29 @@ const failable<value, std::string> evalExpr(const value& expr, const std::string // 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)) + if (!hasContent(jsreq)) return mkfailure<value, std::string>(reason(jsreq)); if (logContent) { std::cout<< "content: " << std::endl; - write(jsreq, std::cout); + write(content(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)) + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, content(jsreq)), rcons<std::string>, list<std::string>(), url, "POST", ch); + if (!hasContent(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); + write(cadr<list<std::string> >(content(res)), std::cout); std::cout << std::endl; } - const list<value> val = elementsToValues(json::readJSON(cadr<list<std::string> >(res), cx)); + const list<value> val = elementsToValues(content(json::readJSON(cadr<list<std::string> >(content(res)), cx))); return cadr<value>(cadr<value>(val)); } @@ -250,9 +250,8 @@ const failable<value, std::string> get(const std::string& url, const CURLHandle& // 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)) + if (!hasContent(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) { @@ -261,7 +260,7 @@ const failable<value, std::string> get(const std::string& url, const CURLHandle& // Return the content as a string value std::ostringstream os; - write(reverse(cadr(ls)), os); + write(reverse(cadr(content(res))), os); return value(os.str()); } @@ -272,19 +271,19 @@ const failable<value, std::string> post(const value& val, const std::string& url // Convert value to an ATOM entry const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); - if (!hasValue(entry)) + if (!hasContent(entry)) return mkfailure<value, std::string>(reason(entry)); if (logContent) { std::cout << "content:" << std::endl; - write(list<std::string>(entry), std::cout); + write(list<std::string>(content(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 list<list<std::string> > req = mklist<list<std::string> >(h, content(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)) + if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); return value(true); } @@ -296,19 +295,19 @@ const failable<value, std::string> put(const value& val, const std::string& url, // Convert value to an ATOM entry const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); - if (!hasValue(entry)) + if (!hasContent(entry)) return mkfailure<value, std::string>(reason(entry)); if (logContent) { std::cout << "content:" << std::endl; - write(list<std::string>(entry), std::cout); + write(list<std::string>(content(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 list<list<std::string> > req = mklist<list<std::string> >(h, content(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)) + if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); return value(true); } @@ -319,7 +318,7 @@ const failable<value, std::string> put(const value& val, const std::string& url, 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)) + if (!hasContent(res)) return mkfailure<value, std::string>(reason(res)); return value(true); } @@ -333,9 +332,9 @@ struct proxy { const value operator()(const list<value>& args) const { failable<value, std::string> val = evalExpr(args, url, ch); - if (!hasValue(val)) + if (!hasContent(val)) return value(); - return val; + return content(val); } const std::string url; diff --git a/sca-cpp/trunk/modules/http/http-test b/sca-cpp/trunk/modules/http/http-test index d70db8d469..1ab0da64b9 100755 --- a/sca-cpp/trunk/modules/http/http-test +++ b/sca-cpp/trunk/modules/http/http-test @@ -18,17 +18,7 @@ # 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 - +./httpd-conf tmp 8090 htdocs apachectl -k start -d `pwd`/tmp sleep 1 diff --git a/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf index 10a5b47ac2..b00ee06ed3 100755 --- a/sca-cpp/trunk/modules/http/httpd-conf +++ b/sca-cpp/trunk/modules/http/httpd-conf @@ -31,7 +31,5 @@ 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 index 1d9b3cb34d..57c35c5cc9 100755 --- a/sca-cpp/trunk/modules/http/httpd-test +++ b/sca-cpp/trunk/modules/http/httpd-test @@ -21,16 +21,6 @@ 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 @@ -39,37 +29,6 @@ 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 diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp index 1271afc03c..a9ced05208 100644 --- a/sca-cpp/trunk/modules/http/httpd.hpp +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -23,7 +23,7 @@ #define tuscany_httpd_hpp /** - * HTTPD module utility functions. + * HTTPD module implementation functions. */ #include <string> @@ -62,6 +62,32 @@ bool logRequests = false; bool logContent = false; /** + * Returns a server-scoped module configuration. + */ +template<typename C> void* makeServerConf(apr_pool_t *p, server_rec *s) { + C* c = new (apr_palloc(p, sizeof(C))) C(s); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<C>, apr_pool_cleanup_null) ; + return c; +} + +template<typename C> const C& serverConf(const request_rec* r, const module* mod) { + return *(C*)ap_get_module_config(r->server->module_config, mod); +} + +/** + * Returns a directory-scoped module configuration. + */ +template<typename C> void *makeDirConf(apr_pool_t *p, char *dirspec) { + C* c = new (apr_palloc(p, sizeof(C))) C(dirspec); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<C>, apr_pool_cleanup_null) ; + return c; +} + +template<typename C> C& dirConf(const request_rec* r, const module* mod) { + return *(C*)ap_get_module_config(r->per_dir_config, mod); +} + +/** * Convert a path string to a list of values. */ const list<std::string> pathTokens(const char* p) { @@ -111,6 +137,8 @@ int logHeader(void* r, const char* key, const char* value) { } const bool logRequest(request_rec* r, const std::string& msg) { + if (!logRequests) + return true; std::cout << msg << std::endl; std::cout << "protocol: " << optional(r->protocol) << std::endl; std::cout << "method: " << optional(r->method) << std::endl; @@ -128,6 +156,32 @@ const bool logRequest(request_rec* r, const std::string& msg) { return true; } +const bool logValue(const value& v, const std::string& msg) { + if (!logContent) + return true; + std::cout<< msg << ": " << v << std::endl; + std::cout.flush(); + return true; +} + +const bool logValue(const failable<value, std::string>& v, const std::string& msg) { + if (!logContent) + return true; + std::cout<< msg << ": " << v << std::endl; + std::cout.flush(); + return true; +} + +const bool logStrings(const list<std::string>& ls, const std::string& msg) { + if (!logContent) + return true; + std::cout<< msg << ": " << std::endl; + write(ls, std::cout); + std::cout<< std::endl; + std::cout.flush(); + return true; +} + /** * Returns a list of key value pairs from the args in a query string. */ @@ -144,6 +198,19 @@ const list<list<value> > queryArgs(const request_rec* r) { } /** + * 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))); +} + +/** * Converts the args received in a POST to a list of key value pairs. */ const list<list<value> > postArgs(const list<value>& a) { @@ -153,6 +220,83 @@ const list<list<value> > postArgs(const list<value>& a) { return cons(l, postArgs(cdr(a))); } +/** + * Setup the HTTP read policy. + */ +const int setupReadPolicy(request_rec* r) { + const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); + if(rc != OK) + return rc; + ap_should_client_block(r); + if(r->read_chunked == true && r->remaining == 0) + r->chunked = true; + //apr_table_setn(r->headers_out, "Connection", "close"); + return OK; +} + +/** + * Read the content of a POST or PUT. + */ +const list<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)))); +} + +/** + * 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 (!hasContent(ls)) + return mkfailure<int, std::string>(reason(ls)); + std::ostringstream os; + write(content(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; +} + +/** + * Report request execution status. + */ +const int reportStatus(const failable<int, std::string>& rc) { + if (!hasContent(rc)) + return HTTP_INTERNAL_SERVER_ERROR; + return content(rc); +} + } } diff --git a/sca-cpp/trunk/modules/http/mod-eval.cpp b/sca-cpp/trunk/modules/http/mod-eval.cpp deleted file mode 100644 index 6fef2be2cb..0000000000 --- a/sca-cpp/trunk/modules/http/mod-eval.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT 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/json/json-test.cpp b/sca-cpp/trunk/modules/json/json-test.cpp index fd506cea65..4d1bbffd5a 100644 --- a/sca-cpp/trunk/modules/json/json-test.cpp +++ b/sca-cpp/trunk/modules/json/json-test.cpp @@ -70,11 +70,11 @@ bool testJSON() { std::istringstream is(os.str()); const list<std::string> il = streamList(is); - const list<value> r = readJSON(il, cx); + const list<value> r = content(readJSON(il, cx)); assert(r == l); std::ostringstream wos; - write(writeJSON(r, cx), wos); + write(content(writeJSON(r, cx)), wos); assert(wos.str() == os.str()); } return true; @@ -84,7 +84,7 @@ 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> e = content(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"))); @@ -92,16 +92,16 @@ bool testJSONRPC() { } { 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 list<value> e = content(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); + const list<value> e2 = content(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); + const list<value> e = content(readJSON(mklist(i), cx)); std::ostringstream os; - write(writeJSON(e, cx), os); + write(content(writeJSON(e, cx)), os); assert(os.str() == i); const list<value> v = elementsToValues(e); const list<value> r = valuesToElements(v); @@ -111,16 +111,16 @@ bool testJSONRPC() { 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); + write(content(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> r = content(readJSON(mklist(f), cx)); const list<value> v = elementsToValues(r); const list<value> e = valuesToElements(v); std::ostringstream os; - write(writeJSON(e, cx), os); + write(content(writeJSON(e, cx)), os); assert(os.str() == f); } return true; diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp index f6c8eb5fe8..7b18d237ec 100644 --- a/sca-cpp/trunk/modules/json/json.hpp +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -217,7 +217,7 @@ const failable<list<value>, std::string> readJSON(const list<std::string>& ilist if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL)) return mkfailure<list<value>, std::string>("JS_FinishJSONParse failed"); - if(!hasValue(consumed)) + if(!hasContent(consumed)) return mkfailure<list<value>, std::string>(reason(consumed)); return list<value>(jsValToValue(val, cx)); @@ -318,7 +318,7 @@ const failable<bool, std::string> writeList(const list<value>& l, JSObject* o, c // Write its children const failable<bool, std::string> w = writeList(elementChildren(token), child, cx); - if (!hasValue(w)) + if (!hasContent(w)) return w; } } @@ -356,7 +356,7 @@ template<typename R> const failable<R, std::string> writeJSON(const lambda<R(std 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)) + if (!hasContent(w)) return mkfailure<R, std::string>(reason(w)); WriteContext<R> wcx(reduce, initial, cx); @@ -370,9 +370,9 @@ template<typename R> const failable<R, std::string> writeJSON(const lambda<R(std */ 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)) + if (!hasContent(ls)) return ls; - return reverse(list<std::string>(ls)); + return reverse(list<std::string>(content(ls))); } /** diff --git a/sca-cpp/trunk/modules/scdl/scdl-test b/sca-cpp/trunk/modules/scdl/scdl-test Binary files differdeleted file mode 100755 index acef45d225..0000000000 --- a/sca-cpp/trunk/modules/scdl/scdl-test +++ /dev/null diff --git a/sca-cpp/trunk/modules/server/Makefile.am b/sca-cpp/trunk/modules/server/Makefile.am new file mode 100644 index 0000000000..204c8e4ae4 --- /dev/null +++ b/sca-cpp/trunk/modules/server/Makefile.am @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = client-test + +libdir=$(prefix)/lib +lib_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${HTTPD_INCLUDE} -I${APR_INCLUDE} -I${JS_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${JS_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${JS_LIB} -lmozjs + +client_test_SOURCES = client-test.cpp +client_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${JS_LIB} -lmozjs + +TESTS = httpd-test http-test wiring-test + + diff --git a/sca-cpp/trunk/modules/server/client-test.cpp b/sca-cpp/trunk/modules/server/client-test.cpp new file mode 100644 index 0000000000..b43cb92c52 --- /dev/null +++ b/sca-cpp/trunk/modules/server/client-test.cpp @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "../http/curl.hpp" + +namespace tuscany { +namespace server { + +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() { + http::CURLHandle ch; + { + std::ostringstream os; + const failable<list<std::ostringstream*>, std::string> r = http::get<std::ostringstream*>(curlWriter, &os, "http://localhost:8090", ch); + assert(hasContent(r)); + assert(contains(os.str(), "HTTP/1.1 200 OK")); + assert(contains(os.str(), "It works")); + } + { + const failable<value, std::string> r = http::get("http://localhost:8090", ch); + assert(hasContent(r)); + assert(contains(content(r), "It works")); + } + return true; +} + +const bool testGetLoop(const int count, http::CURLHandle& ch) { + if (count == 0) + return true; + const failable<value, std::string> r = get("http://localhost:8090", ch); + assert(hasContent(r)); + assert(contains(content(r), "It works")); + return testGetLoop(count - 1, ch); +} + +const bool testGetPerf() { + const int count = 50; + http::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() { + http::CURLHandle ch; + const value val = content(http::evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8090/test", ch)); + assert(val == std::string("Hello")); + return true; +} + +const bool testEvalLoop(const int count, http::CURLHandle& ch) { + if (count == 0) + return true; + const value val = content(http::evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8090/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, http::CURLHandle& ch) { + if (count == 0) + return true; + const value val = content(http::evalExpr(mklist<value>(std::string("echo"), blobs), "http://localhost:8090/test", ch)); + assert(val == blobs); + return testBlobEvalLoop(count - 1, ch); +} + +const bool testEvalPerf() { + const int count = 50; + http::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); + http::CURLHandle ch; + value rc = content(http::post(a, "http://localhost:8090/test", ch)); + assert(rc == value(true)); + return true; +} + +const bool testPostLoop(const int count, const value& val, http::CURLHandle& ch) { + if (count == 0) + return true; + const value rc = content(http::post(val, "http://localhost:8090/test", ch)); + assert(rc == value(true)); + return testPostLoop(count - 1, val, ch); +} + +const bool testPostPerf() { + const int count = 50; + http::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); + http::CURLHandle ch; + value rc = content(http::put(a, "http://localhost:8090/test/111", ch)); + assert(rc == value(true)); + return true; +} + +const bool testDel() { + http::CURLHandle ch; + value rc = content(http::del("http://localhost:8090/test/123456789", ch)); + assert(rc == value(true)); + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::server::testGet(); + tuscany::server::testGetPerf(); + tuscany::server::testPost(); + tuscany::server::testPostPerf(); + tuscany::server::testEval(); + tuscany::server::testEvalPerf(); + tuscany::server::testFeed(); + tuscany::server::testPut(); + tuscany::server::testDel(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/http/htdocs/entry.xml b/sca-cpp/trunk/modules/server/htdocs/entry.xml index 86b8a10547..86b8a10547 100644 --- a/sca-cpp/trunk/modules/http/htdocs/entry.xml +++ b/sca-cpp/trunk/modules/server/htdocs/entry.xml diff --git a/sca-cpp/trunk/modules/http/htdocs/feed.xml b/sca-cpp/trunk/modules/server/htdocs/feed.xml index 5e37de6580..5e37de6580 100644 --- a/sca-cpp/trunk/modules/http/htdocs/feed.xml +++ b/sca-cpp/trunk/modules/server/htdocs/feed.xml diff --git a/sca-cpp/trunk/modules/server/htdocs/index.html b/sca-cpp/trunk/modules/server/htdocs/index.html new file mode 100644 index 0000000000..1bfb3e30c2 --- /dev/null +++ b/sca-cpp/trunk/modules/server/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/server/htdocs/json-request.txt index b4bd07fc46..b4bd07fc46 100644 --- a/sca-cpp/trunk/modules/http/htdocs/json-request.txt +++ b/sca-cpp/trunk/modules/server/htdocs/json-request.txt diff --git a/sca-cpp/trunk/modules/http/htdocs/json-result.txt b/sca-cpp/trunk/modules/server/htdocs/json-result.txt index 121bf74902..121bf74902 100644 --- a/sca-cpp/trunk/modules/http/htdocs/json-result.txt +++ b/sca-cpp/trunk/modules/server/htdocs/json-result.txt diff --git a/sca-cpp/trunk/modules/server/http-test b/sca-cpp/trunk/modules/server/http-test new file mode 100755 index 0000000000..6d23911c31 --- /dev/null +++ b/sca-cpp/trunk/modules/server/http-test @@ -0,0 +1,43 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +../http/httpd-conf tmp 8090 htdocs +./server-conf tmp +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 +./client-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/server/httpd-client.scm index 12275693f4..12275693f4 100644 --- a/sca-cpp/trunk/modules/http/httpd-client.scm +++ b/sca-cpp/trunk/modules/server/httpd-client.scm diff --git a/sca-cpp/trunk/modules/server/httpd-test b/sca-cpp/trunk/modules/server/httpd-test new file mode 100755 index 0000000000..7fa2112f75 --- /dev/null +++ b/sca-cpp/trunk/modules/server/httpd-test @@ -0,0 +1,80 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." + +# Setup +../http/httpd-conf tmp 8090 htdocs +./server-conf tmp +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/server/httpd-test.composite index 875d26ae1b..875d26ae1b 100644 --- a/sca-cpp/trunk/modules/http/httpd-test.composite +++ b/sca-cpp/trunk/modules/server/httpd-test.composite diff --git a/sca-cpp/trunk/modules/http/httpd-test.scm b/sca-cpp/trunk/modules/server/httpd-test.scm index 0566eaf36f..0566eaf36f 100644 --- a/sca-cpp/trunk/modules/http/httpd-test.scm +++ b/sca-cpp/trunk/modules/server/httpd-test.scm diff --git a/sca-cpp/trunk/modules/server/mod-cpp.hpp b/sca-cpp/trunk/modules/server/mod-cpp.hpp new file mode 100644 index 0000000000..cb24b76f6c --- /dev/null +++ b/sca-cpp/trunk/modules/server/mod-cpp.hpp @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modcpp_hpp +#define tuscany_modcpp_hpp + +/** + * Evaluation functions used by mod-eval to evaluate implementation.cpp + * component implementations. + */ + +#include <string> +#include <iostream> +#include <fstream> + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "dynlib.hpp" +#include "cache.hpp" +#include "../eval/driver.hpp" +#include "../http/httpd.hpp" +#include "mod-eval.hpp" + +namespace tuscany { +namespace server { +namespace modeval { +namespace cpp { + +/** + * Evaluate a C++ component implementation function. + */ +struct evalImplementation { + const lib ilib; + const ilambda impl; + evalImplementation(const lib& ilib, const ilambda& impl) : ilib(ilib), impl(impl) { + } + const failable<value, std::string> operator()(const value& func, const list<value>& params) const { + httpd::logValue(cons<value>(func, params), "expr"); + const failable<value, std::string> val = impl(func, params); + httpd::logValue(content(val), "val"); + return val; + } +}; + +/** + * Read a C++ component implementation. + */ +const failable<ilambda, std::string> readLatestImplementation(const std::string path) { + const failable<lib, std::string> ilib(dynlib(path)); + if (!hasContent(ilib)) + return mkfailure<ilambda, std::string>(reason(ilib)); + + const failable<ilambda, std::string> impl(dynlambda<failable<value, std::string>(value, list<value>)>("eval", content(ilib))); + if (!hasContent(impl)) + return impl; + return ilambda(evalImplementation(content(ilib), content(impl))); +} + +const cached<failable<ilambda, std::string> > readImplementation(const std::string& path) { + const lambda<failable<ilambda, std::string>(std::string)> ri(readLatestImplementation); + const lambda<unsigned long(std::string)> ft(latestFileTime); + const std::string p(path + dynlibExt); + return cached<failable<ilambda, std::string> >(curry(ri, p), curry(ft, p)); +} + +} +} +} +} + +#endif /* tuscany_modcpp_hpp */ diff --git a/sca-cpp/trunk/modules/server/mod-eval.cpp b/sca-cpp/trunk/modules/server/mod-eval.cpp new file mode 100644 index 0000000000..f843b9bdc5 --- /dev/null +++ b/sca-cpp/trunk/modules/server/mod-eval.cpp @@ -0,0 +1,387 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 <string> +#include <iostream> +#include <sstream> +#include <fstream> + +#include "function.hpp" +#include "list.hpp" +#include "slist.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "cache.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../scdl/scdl.hpp" +#include "../http/curl.hpp" +#include "../http/httpd.hpp" +#include "mod-eval.hpp" +#include "mod-scm.hpp" +#include "mod-cpp.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(server_rec* s) : s(s), home("") { + } + server_rec* s; + std::string home; +}; + +/** + * 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(char* dirspec) : dirspec(dirspec), contributionPath(""), compositeName(""), componentName(""), implementationPath("") { + } + char* dirspec; + std::string contributionPath; + std::string compositeName; + std::string componentName; + std::string implementationPath; + cached<failable<value, std::string> > component; + cached<failable<ilambda, std::string> > implementation; +}; + +/** + * Handle an HTTP GET. + */ +const failable<int, std::string> get(request_rec* r, const ilambda& impl, const list<value>& px) { + + // Inspect the query string + const list<list<value> > args = httpd::queryArgs(r); + const list<value> ia = assoc(value("id"), args); + const list<value> ma = assoc(value("method"), args); + + // Evaluate a JSON-RPC request and return a JSON result + if (!isNil(ia) && !isNil(ma)) { + + // Extract the request id, method and params + const value id = cadr(ia); + const value func = std::string(cadr(ma)).c_str(); + const list<value> params = httpd::queryParams(args); + + // Apply the requested function + const failable<value, std::string> val = impl(func, append(params, px)); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return JSON result + json::JSONContext cx; + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate an ATOM GET request and return an ATOM feed + const list<value> id(httpd::path(r->path_info)); + if (isNil(id)) { + const failable<value, std::string> val = impl("getall", px); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(content(val))), "application/atom+xml;type=feed", r); + } + + // Evaluate an ATOM GET and return an ATOM entry + const failable<value, std::string> val = impl("get", cons<value>(car(id), px)); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(content(val))), "application/atom+xml;type=entry", r); +} + +/** + * Handle an HTTP POST. + */ +const failable<int, std::string> post(request_rec* r, const ilambda& impl, const list<value>& px) { + const list<std::string> ls = httpd::read(r); + httpd::logStrings(ls, "content"); + + // Evaluate a JSON-RPC request and return a JSON result + const std::string ct = httpd::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(content(json::readJSON(ls, cx))); + const list<list<value> > args = httpd::postArgs(json); + + // Extract the request id, method and params + const value id = cadr(assoc(value("id"), args)); + const value func = 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 = impl(func, append(params, px)); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return JSON result + return httpd::writeResult(json::jsonResult(id, content(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 = httpd::feedEntry(content(atom::readEntry(ls))); + const failable<value, std::string> val = impl("post", cons<value>(entry, px)); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return the created resource location + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, httpd::url(content(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 ilambda& impl, const list<value>& px) { + const list<std::string> ls = httpd::read(r); + httpd::logStrings(ls, "content"); + + // Evaluate an ATOM PUT request + const list<value> id(httpd::path(r->path_info)); + const value entry = httpd::feedEntry(content(atom::readEntry(ls))); + const failable<value, std::string> val = impl("put", append(mklist<value>(entry, car(id)), px)); + if (!hasContent(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 ilambda& impl, const list<value>& px) { + + // Evaluate an ATOM delete request + const list<value> id(httpd::path(r->path_info)); + const failable<value, std::string> val = impl("delete", cons<value>(car(id), px)); + if (!hasContent(val)) + return mkfailure<int, std::string>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * 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 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(latestFileTime); + return cached<failable<value, std::string> >(curry(rc, path, conf->componentName), 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)); +} + +/** + * Returns the component implementation with the given implementation type and path. + * For now only Scheme and C++ implementations are supported. + */ +const cached<failable<ilambda, std::string> > implementation(const std::string& itype, const std::string& path) { + if (itype.find(".scheme") != std::string::npos) + return latest(scm::readImplementation(path)); + if (itype.find(".cpp") != std::string::npos) + return latest(cpp::readImplementation(path)); + return cached<failable<ilambda, std::string> >(); +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + httpd::logRequest(r, "mod_tuscany_eval::handler"); + + // Set up the read policy + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + + // Retrieve the latest component configuration + DirConf& conf = httpd::dirConf<DirConf>(r, &mod_tuscany_eval); + conf.component = latest(conf.component); + const failable<value, std::string> comp(content(conf.component)); + if (!hasContent(comp)) + return HTTP_NOT_FOUND; + + // Retrieve the latest implementation + const value ielement= scdl::implementation(content(comp)); + const std::string path = conf.contributionPath + std::string(scdl::uri(ielement)); + if (path != conf.implementationPath) { + conf.implementationPath = path; + conf.implementation = implementation(elementName(ielement), path); + } + else + conf.implementation = latest(conf.implementation); + const failable<ilambda, std::string> impl(content(conf.implementation)); + if (!hasContent(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(content(comp))) << "/"; + http::CURLHandle ch; + const list<value> px(proxies(scdl::references(content(comp)), base.str(), ch)); + + // Handle HTTP method + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return httpd::reportStatus(get(r, content(impl), px)); + if(r->method_number == M_POST) + return httpd::reportStatus(post(r, content(impl), px)); + if(r->method_number == M_PUT) + return httpd::reportStatus(put(r, content(impl), px)); + if(r->method_number == M_DELETE) + return httpd::reportStatus(del(r, content(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; +} + +/** + * 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 and merger + tuscany::httpd::makeDirConf<tuscany::server::modeval::DirConf>, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::server::modeval::ServerConf>, NULL, + // commands and hooks + tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks +}; + +} diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp new file mode 100644 index 0000000000..a350538956 --- /dev/null +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modeval_hpp +#define tuscany_modeval_hpp + +/** + * Defines the signature of component implementation lambdas + * expected by mod-eval. + */ + +#include <string> + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Represents a component implementation lambda function. + */ +typedef lambda<failable<value, std::string>(value, list<value>)> ilambda; + +} +} +} + +#endif /* tuscany_modeval_hpp */ diff --git a/sca-cpp/trunk/modules/server/mod-scm.hpp b/sca-cpp/trunk/modules/server/mod-scm.hpp new file mode 100644 index 0000000000..386d032695 --- /dev/null +++ b/sca-cpp/trunk/modules/server/mod-scm.hpp @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modscm_hpp +#define tuscany_modscm_hpp + +/** + * Evaluation functions used by mod-eval to evaluate implementation.scheme + * component implementations. + */ + +#include <string> +#include <iostream> +#include <fstream> + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "cache.hpp" +#include "../eval/driver.hpp" +#include "../http/httpd.hpp" +#include "mod-eval.hpp" + +namespace tuscany { +namespace server { +namespace modeval { +namespace scm { + +/** + * Evaluate a script component implementation function. + */ +struct evalImplementation { + const value impl; + evalImplementation(const value& impl) : impl(impl) { + } + const failable<value, std::string> operator()(const value& func, const list<value>& params) const { + const value expr = cons<value>(func, eval::quotedParameters(params)); + httpd::logValue(expr, "expr"); + gc_pool pool; + eval::Env globalEnv = eval::setupEnvironment(pool); + const value val = eval::evalScript(expr, impl, globalEnv, pool); + httpd::logValue(val, "val"); + if (isNil(val)) + return mkfailure<value, std::string>("Could not evaluate expression"); + return val; + } +}; + +/** + * Read a script component implementation. + */ +const failable<ilambda, std::string> readLatestImplementation(const std::string path) { + std::ifstream is(path.c_str(), std::ios_base::in); + if (is.fail() || is.bad()) + return mkfailure<ilambda, std::string>("Could not read implementation: " + path); + const value impl = eval::readScript(is); + if (isNil(impl)) + return mkfailure<ilambda, std::string>("Could not read implementation: " + path); + return ilambda(evalImplementation(impl)); +} + +const cached<failable<ilambda, std::string> > readImplementation(const std::string& path) { + const lambda<failable<ilambda, std::string>(std::string)> ri(readLatestImplementation); + const lambda<unsigned long(std::string)> ft(latestFileTime); + return cached<failable<ilambda, std::string> >(curry(ri, path), curry(ft, path)); +} + +} +} +} +} + +#endif /* tuscany_modscm_hpp */ diff --git a/sca-cpp/trunk/modules/http/mod-wiring.cpp b/sca-cpp/trunk/modules/server/mod-wiring.cpp index 965d5a87fb..2d3c8ce045 100644 --- a/sca-cpp/trunk/modules/http/mod-wiring.cpp +++ b/sca-cpp/trunk/modules/server/mod-wiring.cpp @@ -20,7 +20,7 @@ /* $Rev$ $Date$ */ /** - * HTTPD module used to wire components. + * HTTPD module used to wire component references. */ #include <sys/stat.h> @@ -34,16 +34,16 @@ #include "slist.hpp" #include "value.hpp" #include "monad.hpp" +#include "cache.hpp" #include "../scdl/scdl.hpp" -#include "../cache/cache.hpp" -#include "httpd.hpp" +#include "../http/httpd.hpp" extern "C" { extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring; } namespace tuscany { -namespace httpd { +namespace server { namespace modwiring { /** @@ -51,15 +51,12 @@ namespace modwiring { */ class ServerConf { public: - std::string home; - ServerConf() : home("") { + ServerConf(server_rec* s) : home("") { } + server_rec* s; + std::string 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. */ @@ -70,17 +67,14 @@ const bool useModProxy = true; */ class DirConf { public: - DirConf() : contributionPath(""), compositeName("") { + DirConf(char* dirspec) : contributionPath(""), compositeName("") { } + char* dirspec; std::string contributionPath; std::string compositeName; - cache::cached<failable<list<value>, std::string> > components; + 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. */ @@ -91,11 +85,11 @@ const failable<list<value>, std::string> readComponents(const std::string& path) return scdl::components(readXML(streamList(is))); } -const cache::cached<failable<list<value>, std::string> > components(DirConf* conf) { +const 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)); + const lambda<unsigned long(std::string)> ft(latestFileTime); + return cached<failable<list<value>, std::string> >(curry(rc, path), curry(ft, path)); } /** @@ -112,19 +106,16 @@ const bool isAbsolute(const std::string& uri) { 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"); + httpd::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)) + DirConf& conf = httpd::dirConf<DirConf>(r, &mod_tuscany_wiring); + conf.components = latest(conf.components); + const failable<list<value>, std::string> comps(content(conf.components)); + if (!hasContent(comps)) return HTTP_INTERNAL_SERVER_ERROR; - const value comp(scdl::named(cadr(rpath), list<value>(comps))); + const list<value> rpath(httpd::path(r->uri)); + const value comp(scdl::named(cadr(rpath), list<value>(content(comps)))); if (isNil(comp)) return HTTP_NOT_FOUND; const value ref(scdl::named(caddr(rpath), scdl::references(comp))); @@ -174,10 +165,7 @@ const std::string redirect(const std::string& file, const std::string& pi, const 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"); + httpd::logRequest(r, "mod_tuscany_wiring::handler"); // Do an internal redirect if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) @@ -211,17 +199,6 @@ const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { 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. */ @@ -259,18 +236,12 @@ 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 + // dir config and merger + tuscany::httpd::makeDirConf<tuscany::server::modwiring::DirConf>, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::server::modwiring::ServerConf>, NULL, + // commands and hooks + tuscany::server::modwiring::commands, tuscany::server::modwiring::registerHooks }; } diff --git a/sca-cpp/trunk/modules/server/server-conf b/sca-cpp/trunk/modules/server/server-conf new file mode 100755 index 0000000000..dfe4265bae --- /dev/null +++ b/sca-cpp/trunk/modules/server/server-conf @@ -0,0 +1,32 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +mkdir -p $root +mkdir -p $root/logs +mkdir -p $root/conf +cat >>$root/conf/httpd.conf <<EOF +LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so +LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so +LoadModule mod_tuscany_cache $here/.libs/libmod_tuscany_cache.so +EOF + diff --git a/sca-cpp/trunk/modules/http/wiring-test b/sca-cpp/trunk/modules/server/wiring-test index 0c3f36b513..c50732ddb2 100755 --- a/sca-cpp/trunk/modules/http/wiring-test +++ b/sca-cpp/trunk/modules/server/wiring-test @@ -20,7 +20,8 @@ echo "Testing..." # Setup -./httpd-conf tmp 8092 htdocs +../http/httpd-conf tmp 8090 htdocs +./server-conf tmp cat >>tmp/conf/httpd.conf <<EOF <Location /test> @@ -48,37 +49,37 @@ apachectl -k start -d `pwd`/tmp sleep 1 # Test HTTP GET -curl http://localhost:8092/index.html 2>/dev/null >tmp/index.html +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:8092/client/ >tmp/feed.xml 2>/dev/null + curl http://localhost:8090/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 + curl http://localhost:8090/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 + curl http://localhost:8090/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 + curl http://localhost:8090/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 + curl http://localhost:8090/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 + curl http://localhost:8090/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 diff --git a/sca-cpp/trunk/test/store-script/store-http-test b/sca-cpp/trunk/test/store-script/store-http-test index 551731e856..d04eab8a6c 100755 --- a/sca-cpp/trunk/test/store-script/store-http-test +++ b/sca-cpp/trunk/test/store-script/store-http-test @@ -20,7 +20,8 @@ echo "Testing..." # Setup -../../modules/http/httpd-conf tmp 8093 htdocs +../../modules/http/httpd-conf tmp 8090 htdocs +../../modules/server/server-conf tmp cat >>tmp/conf/httpd.conf <<EOF <Location /Catalog> @@ -62,7 +63,7 @@ apachectl -k start -d `pwd`/tmp sleep 1 # Test HTTP GET -curl http://localhost:8093/store.html 2>/dev/null >tmp/store.html +curl http://localhost:8090/store.html 2>/dev/null >tmp/store.html diff tmp/store.html htdocs/store.html rc=$? |