diff options
18 files changed, 565 insertions, 96 deletions
diff --git a/sca-cpp/trunk/components/webservice/Makefile.am b/sca-cpp/trunk/components/webservice/Makefile.am index f4dc712b4f..fd85cf4cb0 100644 --- a/sca-cpp/trunk/components/webservice/Makefile.am +++ b/sca-cpp/trunk/components/webservice/Makefile.am @@ -17,7 +17,7 @@ if WANT_WEBSERVICE -noinst_PROGRAMS = webservice-test client-test +noinst_PROGRAMS = axiom-test axis2-test client-test INCLUDES = -I${AXIS2C_INCLUDE} @@ -27,12 +27,15 @@ comp_LTLIBRARIES = libwebservice.la libwebservice_la_SOURCES = webservice.cpp libwebservice_la_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -webservice_test_SOURCES = webservice-test.cpp -webservice_test_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine +axiom_test_SOURCES = axiom-test.cpp +axiom_test_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine + +axis2_test_SOURCES = axis2-test.cpp +axis2_test_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine client_test_SOURCES = client-test.cpp -client_test_LDFLAGS = -lxml2 -lcurl -lmozjs +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs -TESTS = webservice-test client-test +TESTS = axiom-test echo-test server-test endif diff --git a/sca-cpp/trunk/components/webservice/axiom-test.cpp b/sca-cpp/trunk/components/webservice/axiom-test.cpp new file mode 100644 index 0000000000..c52183ea9b --- /dev/null +++ b/sca-cpp/trunk/components/webservice/axiom-test.cpp @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test Web service Axiom support functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "list.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "value.hpp" +#include "perf.hpp" +#include "webservice.hpp" + +namespace tuscany { +namespace webservice { + +const string customerElement = +"<customer>" +"<name>jdoe</name>" +"<address><city>san francisco</city><state>ca</state></address>" +"<account><id>1234</id><balance>1000</balance></account>" +"<account><id>6789</id><balance>2000</balance></account>" +"<account><id>4567</id><balance>3000</balance></account>" +"</customer>"; + +bool testAxiom() { + const Axis2Context ax; + { + const failable<axiom_node_t*> n = stringToAxiomNode(customerElement, ax); + assert(hasContent(n)); + const failable<const string> c = axiomNodeToString(content(n), ax); + assert(hasContent(c)); + assert(content(c) == customerElement); + } + { + const list<value> arg = mklist<value>( + list<value>() + "ns1:echoString" + + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + + (list<value>() + "text" + string("Hello World!"))); + const failable<axiom_node_t*> n = valuesToAxiomNode(arg, ax); + assert(hasContent(n)); + const failable<const string> x = axiomNodeToString(content(n), ax); + assert(hasContent(x)); + assert(content(x) == "<ns1:echoString xmlns:ns1=\"http://ws.apache.org/axis2/services/echo\"><text>Hello World!</text></ns1:echoString>"); + const failable<const list<value> > l = axiomNodeToValues(content(n), ax); + assert(hasContent(l)); + assert(l == arg); + } + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::webservice::testAxiom(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/trunk/components/webservice/webservice-test.cpp b/sca-cpp/trunk/components/webservice/axis2-test.cpp index e544b193c8..8ab21bee29 100644 --- a/sca-cpp/trunk/components/webservice/webservice-test.cpp +++ b/sca-cpp/trunk/components/webservice/axis2-test.cpp @@ -20,13 +20,16 @@ /* $Rev$ $Date$ */ /** - * Test WebService component. + * Test WebService Axis2 client support functions. */ #include <assert.h> #include "stream.hpp" #include "string.hpp" #include "list.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "value.hpp" #include "perf.hpp" #include "webservice.hpp" @@ -34,6 +37,23 @@ namespace tuscany { namespace webservice { bool testEval() { + const Axis2Context ax; + + const value func = "http://ws.apache.org/axis2/c/samples/echoString"; + const list<value> arg = mklist<value>( + list<value>() + "ns1:echoString" + + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + + (list<value>() + "text" + string("Hello World!"))); + + const failable<value> rval = evalExpr(mklist<value>(func, arg, string("http://localhost:9090/axis2/services/echo")), ax); + assert(hasContent(rval)); + + const list<value> r = mklist<value>( + list<value>() + "ns1:echoString" + + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples")) + + (list<value>() + "text" + string("Hello World!"))); + assert(content(rval) == r); + return true; } diff --git a/sca-cpp/trunk/components/webservice/client-test.cpp b/sca-cpp/trunk/components/webservice/client-test.cpp index c2593e75d9..6e63c83ecb 100644 --- a/sca-cpp/trunk/components/webservice/client-test.cpp +++ b/sca-cpp/trunk/components/webservice/client-test.cpp @@ -27,6 +27,7 @@ #include "stream.hpp" #include "string.hpp" #include "list.hpp" +#include "element.hpp" #include "value.hpp" #include "monad.hpp" #include "perf.hpp" @@ -35,9 +36,25 @@ namespace tuscany { namespace webservice { -const string url("http://localhost:8090/echo"); +const string url("http://localhost:8090/webservice"); bool testEval() { + http::CURLSession cs; + + const value func = "http://ws.apache.org/axis2/c/samples/echoString"; + const list<value> arg = mklist<value>( + list<value>() + "ns1:echoString" + + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + + (list<value>() + "text" + string("Hello World!"))); + + const failable<value> rval = http::evalExpr(mklist<value>(func, arg), url, cs); + assert(hasContent(rval)); + + const list<value> r = mklist<value>( + list<value>() + "ns1:echoString" + + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples")) + + (list<value>() + "text" + string("Hello World!"))); + assert(content(rval) == r); return true; } diff --git a/sca-cpp/trunk/components/webservice/echo-test b/sca-cpp/trunk/components/webservice/echo-test new file mode 100755 index 0000000000..eedefed7ca --- /dev/null +++ b/sca-cpp/trunk/components/webservice/echo-test @@ -0,0 +1,35 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +axis2="${AXIS2C_HOME}/bin/axis2_http_server" +pwd=`pwd` +cd $AXIS2C_HOME/bin +$axis2 & +cd $pwd +sleep 1 + +# Test +./axis2-test +rc=$? + +# Cleanup +kill `ps -f | grep -v grep | grep "${axis2}" | awk '{ print $2 }'` +sleep 1 +return $rc diff --git a/sca-cpp/trunk/components/webservice/server-test b/sca-cpp/trunk/components/webservice/server-test new file mode 100755 index 0000000000..53765cca46 --- /dev/null +++ b/sca-cpp/trunk/components/webservice/server-test @@ -0,0 +1,49 @@ +#!/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 +../../modules/http/httpd-conf tmp 8090 ../../modules/http/htdocs +../../modules/server/server-conf tmp +../../modules/server/scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF + +<Location /> +SCAContribution `pwd`/ +SCAComposite webservice.composite +</Location> +EOF + +apachectl -k start -d `pwd`/tmp + +axis2="${AXIS2C_HOME}/bin/axis2_http_server" +pwd=`pwd` +cd $AXIS2C_HOME/bin +$axis2 & +cd $pwd +sleep 2 + +# Test +./client-test +rc=$? + +# Cleanup +kill `ps -f | grep -v grep | grep "${axis2}" | awk '{ print $2 }'` +apachectl -k stop -d `pwd`/tmp +sleep 2 +return $rc diff --git a/sca-cpp/trunk/components/webservice/webservice.composite b/sca-cpp/trunk/components/webservice/webservice.composite index a87bce03f6..ebb007b37e 100644 --- a/sca-cpp/trunk/components/webservice/webservice.composite +++ b/sca-cpp/trunk/components/webservice/webservice.composite @@ -24,6 +24,7 @@ <component name="webservice"> <implementation.cpp path=".libs" library="libwebservice"/> + <property name="uri">http://localhost:9090/axis2/services/echo</property> <service name="webservice"> <t:binding.http uri="webservice"/> </service> diff --git a/sca-cpp/trunk/components/webservice/webservice.cpp b/sca-cpp/trunk/components/webservice/webservice.cpp index f4606afdd8..6427756b40 100644 --- a/sca-cpp/trunk/components/webservice/webservice.cpp +++ b/sca-cpp/trunk/components/webservice/webservice.cpp @@ -34,10 +34,20 @@ namespace tuscany { namespace webservice { /** - * Apply a Web service function / operation. + * Apply a Web service function / operation using Axis2. */ -const failable<value> apply(const value& func, unused const list<value>& params) { - return tuscany::mkfailure<tuscany::value>(tuscany::string("Function not supported: ") + func); +const failable<value> apply(const value& func, const list<value>& params) { + const Axis2Context ax; + + // Extract parameters + const value doc = car<value>(params); + const lambda<value(const list<value>&)> l = cadr<value>(params); + + // Call the URI property lambda function to get the configured URI + const value uri = l(list<value>()); + + // Evaluate using Axis2 + return evalExpr(mklist<value>(func, doc, uri), ax); } } diff --git a/sca-cpp/trunk/components/webservice/webservice.hpp b/sca-cpp/trunk/components/webservice/webservice.hpp index 5b7406f595..ba13b9ad7f 100644 --- a/sca-cpp/trunk/components/webservice/webservice.hpp +++ b/sca-cpp/trunk/components/webservice/webservice.hpp @@ -26,14 +26,164 @@ * Web service invocation functions using Axis2. */ +// Ignore redundant declarations in Axiom headers +#ifdef WANT_MAINTAINER_MODE +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif +#include <axiom.h> +#include <axis2_client.h> +#ifdef WANT_MAINTAINER_MODE +#pragma GCC diagnostic warning "-Wredundant-decls" +#endif + #include "string.hpp" +#include "sstream.hpp" #include "list.hpp" #include "value.hpp" +#include "xml.hpp" #include "monad.hpp" namespace tuscany { namespace webservice { +/** + * Represents an Axis2 runtime context. + */ +class Axis2Context { +public: + Axis2Context() : env(axutil_env_create_all("tuscany.log", AXIS2_LOG_LEVEL_WARNING)), owner(true) { + } + + Axis2Context(const Axis2Context& ax) : env(ax.env), owner(false) { + } + + ~Axis2Context() { + if (!owner || env == NULL) + return; + axutil_env_free(env); + } + +private: + axutil_env_t* env; + bool owner; + + friend const axutil_env_t* env(const Axis2Context& ax); +}; + +const axutil_env_t* env(const Axis2Context& ax) { + return ax.env; +} + +/** + * Return the latest Axis2 error in an Axis2 context. + */ +const string axis2Error(const Axis2Context& ax) { + ostringstream os; + os << env(ax)->error->error_number << " : " << AXIS2_ERROR_GET_MESSAGE(env(ax)->error); + return str(os); +} + +/** + * Convert a string to an Axiom node. + */ +const failable<axiom_node_t*> stringToAxiomNode(const string& s, const Axis2Context& ax) { + axiom_node_t* node = axiom_node_create_from_buffer(env(ax), const_cast<axis2_char_t*>(c_str(s))); + if (node == NULL) + return mkfailure<axiom_node_t*>(string("Couldn't convert XML to Axiom node: ") + axis2Error(ax)); + return node; +} + +/** + * Convert a list of values representing XML elements to an Axiom node. + */ +const failable<axiom_node_t*> valuesToAxiomNode(const list<value>& l, const Axis2Context& ax) { + const failable<list<string> > xml = writeXML(valuesToElements(l), false); + if (!hasContent(xml)) + return mkfailure<axiom_node_t*>(reason(xml)); + ostringstream os; + write(content(xml), os); + return stringToAxiomNode(str(os), ax); +} + +/** + * Convert an axiom node to a string. + */ +const failable<const string> axiomNodeToString(axiom_node_t* node, const Axis2Context& ax) { + const char* c = axiom_node_to_string(node, env(ax)); + if (c == NULL) + return mkfailure<const string>(string("Couldn't convert Axiom node to XML: ") + axis2Error(ax)); + const string s(c); + AXIS2_FREE(env(ax)->allocator, const_cast<char*>(c)); + return s; +} + +/** + * Convert an axiom node to a list of values representing XML elements. + */ +const failable<const list<value> > axiomNodeToValues(axiom_node_t* node, const Axis2Context& ax) { + const failable<const string> s = axiomNodeToString(node, ax); + if (!hasContent(s)) + return mkfailure<const list<value> >(reason(s)); + istringstream is(content(s)); + const failable<const list<value> > l = readXML(streamList(is)); + if (!hasContent(l)) + return l; + return elementsToValues(content(l)); +} + +/** + * Evaluate an expression in the form (soap-action-string, document, uri). Send the + * SOAP action and document to the Web Service at the given URI using Axis2. + */ +const char* axis2Home = getenv("AXIS2C_HOME"); + +const failable<value> evalExpr(const value& expr, const Axis2Context& ax) { + debug(expr, "webservice::evalExpr::input"); + + // Extract func name and single argument + const value func(car<value>(expr)); + const list<value> param(cadr<value>(expr)); + const value uri(caddr<value>(expr)); + + // Create Axis2 client + axis2_endpoint_ref_t *epr = axis2_endpoint_ref_create(env(ax), c_str(uri)); + axis2_options_t *opt = axis2_options_create(env(ax)); + axis2_options_set_to(opt, env(ax), epr); + axis2_options_set_action(opt, env(ax), (const axis2_char_t*)c_str(func)); + axis2_svc_client_t *client = axis2_svc_client_create(env(ax), axis2Home); + if (client == NULL) { + axis2_endpoint_ref_free(epr, env(ax)); + axis2_options_free(opt, env(ax)); + return mkfailure<value>("Couldn't create Axis2 client: " + axis2Error(ax)); + } + axis2_svc_client_set_options(client, env(ax), opt); + axis2_svc_client_engage_module(client, env(ax), AXIS2_MODULE_ADDRESSING); + + // Construct request Axiom node + const failable<axiom_node_t*> req = valuesToAxiomNode(param, ax); + if (!hasContent(req)) + return mkfailure<value>(reason(req)); + + // Call the Web service + axiom_node_t* res = axis2_svc_client_send_receive(client, env(ax), content(req)); + if (res == NULL) { + axis2_svc_client_free(client, env(ax)); + return mkfailure<value>("Couldn't invoke Axis2 service: " + axis2Error(ax)); + } + + // Parse result Axiom node + const failable<const list<value> > lval = axiomNodeToValues(res, ax); + if (!hasContent(lval)) + return mkfailure<value>(reason(lval)); + const value rval = content(lval); + debug(rval, "webservice::evalExpr::result"); + + // Cleanup + axis2_svc_client_free(client, env(ax)); + + return rval; +} + } } diff --git a/sca-cpp/trunk/configure.ac b/sca-cpp/trunk/configure.ac index 0f14a157d4..3a35bb1406 100644 --- a/sca-cpp/trunk/configure.ac +++ b/sca-cpp/trunk/configure.ac @@ -349,11 +349,11 @@ fi # Configure path to Axis2C includes and lib. AC_MSG_CHECKING([for axis2c]) AC_ARG_WITH([axis2c], [AC_HELP_STRING([--with-axis2c=PATH], [path to installed Axis2C [default=/usr/local/axis2c]])], [ - AXIS2C_INCLUDE="${withval}/include" + AXIS2C_INCLUDE="${withval}/include/axis2-1.6.0" AXIS2C_LIB="${withval}/lib" AC_MSG_RESULT("${withval}") ], [ - AXIS2C_INCLUDE="/usr/local/axis2c/include" + AXIS2C_INCLUDE="/usr/local/axis2c/include/axis2-1.6.0" AXIS2C_LIB="/usr/local/axis2c/lib" AC_MSG_RESULT(/usr/local/axis2c) ]) diff --git a/sca-cpp/trunk/etc/git-exclude b/sca-cpp/trunk/etc/git-exclude index 023a2c6fc8..e0e49eb902 100644 --- a/sca-cpp/trunk/etc/git-exclude +++ b/sca-cpp/trunk/etc/git-exclude @@ -81,5 +81,6 @@ scdl-test java-test java-shell script-test -webservice-test +axiom-test +axis2-test diff --git a/sca-cpp/trunk/kernel/element.hpp b/sca-cpp/trunk/kernel/element.hpp index 4570110e96..0d14acc4a3 100644 --- a/sca-cpp/trunk/kernel/element.hpp +++ b/sca-cpp/trunk/kernel/element.hpp @@ -37,6 +37,7 @@ namespace tuscany */ const value attribute("attribute"); const value element("element"); +const string atsign("@"); /** * Returns true if a value is an element. @@ -125,7 +126,7 @@ const value elementToValue(const value& t) { // Convert an attribute if (isTaggedList(t, attribute)) - return mklist(attributeName(t), attributeValue(t)); + return mklist<value>(c_str(atsign + attributeName(t)), attributeValue(t)); // Convert an element if (isTaggedList(t, element)) { @@ -196,9 +197,12 @@ const value valueToElement(const value& t) { const value n = car<value>(t); const value v = cadr<value>(t); - // Convert a single value - if (!isList(v)) + // Convert a single value to an attribute or an element + if (!isList(v)) { + if (substr(n, 0, 1) == atsign) + return mklist<value>(attribute, substr(n, 1), v); return mklist(element, n, v); + } // Convert a list value if (isNil((list<value>)v) || !isSymbol(car<value>(v))) diff --git a/sca-cpp/trunk/kernel/xml-test.cpp b/sca-cpp/trunk/kernel/xml-test.cpp index 9e0cde597b..c83a65fd92 100644 --- a/sca-cpp/trunk/kernel/xml-test.cpp +++ b/sca-cpp/trunk/kernel/xml-test.cpp @@ -111,7 +111,7 @@ bool testWriteXML() { return true; } -bool testElement() { +bool testElements() { { const list<value> ad = mklist<value>(mklist<value>("city", string("san francisco")), mklist<value>("state", string("ca"))); const list<value> ac1 = mklist<value>(mklist<value>("id", string("1234")), mklist<value>("balance", 1000)); @@ -149,6 +149,21 @@ bool testElement() { return true; } +bool testValues() { + { + const list<value> l = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!"))); + const list<value> e = valuesToElements(l); + const failable<list<string> > lx = writeXML(e); + ostringstream os; + write(content(lx), os); + istringstream is(str(os)); + const list<value> x = readXML(streamList(is)); + const list<value> v = elementsToValues(x); + assert(v == l); + } + return true; +} + } int main() { @@ -156,7 +171,8 @@ int main() { tuscany::testReadXML(); tuscany::testWriteXML(); - tuscany::testElement(); + tuscany::testElements(); + tuscany::testValues(); tuscany::cout << "OK" << tuscany::endl; diff --git a/sca-cpp/trunk/kernel/xml.hpp b/sca-cpp/trunk/kernel/xml.hpp index 9e903ea34d..fa1701d83a 100644 --- a/sca-cpp/trunk/kernel/xml.hpp +++ b/sca-cpp/trunk/kernel/xml.hpp @@ -290,16 +290,20 @@ const failable<bool> writeList(const list<value>& l, const xmlTextWriterPtr xml) /** * Write a list of values to a libxml2 XML writer. */ -const failable<bool> write(const list<value>& l, const xmlTextWriterPtr xml) { - if (xmlTextWriterStartDocument(xml, NULL, encoding, NULL) < 0) - return mkfailure<bool>(string("xmlTextWriterStartDocument failed")); +const failable<bool> write(const list<value>& l, const xmlTextWriterPtr xml, bool xmlTag) { + if (xmlTag) { + if (xmlTextWriterStartDocument(xml, NULL, encoding, NULL) < 0) + return mkfailure<bool>(string("xmlTextWriterStartDocument failed")); + } const failable<bool> w = writeList(l, xml); if (!hasContent(w)) return w; - if (xmlTextWriterEndDocument(xml) < 0) - return mkfailure<bool>("xmlTextWriterEndDocument failed"); + if (xmlTag) { + if (xmlTextWriterEndDocument(xml) < 0) + return mkfailure<bool>("xmlTextWriterEndDocument failed"); + } return true; } @@ -326,7 +330,7 @@ template<typename R> int writeCallback(void *context, const char* buffer, int le /** * Convert a list of values to an XML document. */ -template<typename R> const failable<R> writeXML(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { +template<typename R> const failable<R> writeXML(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const bool xmlTag) { XMLWriteContext<R> cx(reduce, initial); xmlOutputBufferPtr out = xmlOutputBufferCreateIO(writeCallback<R>, NULL, &cx, NULL); if (out == NULL) @@ -335,7 +339,7 @@ template<typename R> const failable<R> writeXML(const lambda<R(const string&, co if (xml == NULL) return mkfailure<R>("xmlNewTextWriter failed"); - const failable<bool> w = write(l, xml); + const failable<bool> w = write(l, xml, xmlTag); xmlFreeTextWriter(xml); if (!hasContent(w)) { return mkfailure<R>(reason(w)); @@ -343,15 +347,23 @@ template<typename R> const failable<R> writeXML(const lambda<R(const string&, co return cx.accum; } +template<typename R> const failable<R> writeXML(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + return writeXML(reduce, initial, l, true); +} + /** * Convert a list of values to a list of strings representing an XML document. */ -const failable<list<string> > writeXML(const list<value>& l) { - const failable<list<string> > ls = writeXML<list<string> >(rcons<string>, list<string>(), l); +const failable<list<string> > writeXML(const list<value>& l, const bool xmlTag) { + const failable<list<string> > ls = writeXML<list<string> >(rcons<string>, list<string>(), l, xmlTag); if (!hasContent(ls)) return ls; return reverse(list<string>(content(ls))); } +const failable<list<string> > writeXML(const list<value>& l) { + return writeXML(l, true); +} + } #endif /* tuscany_xml_hpp */ diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp index d836eaa2f0..4e96411ec6 100644 --- a/sca-cpp/trunk/modules/http/curl.hpp +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -73,7 +73,7 @@ public: private: CURL* h; - bool owner; + const bool owner; friend CURL* handle(const CURLSession& c); }; @@ -217,15 +217,12 @@ const failable<value> evalExpr(const value& expr, const string& url, const CURLS if (!hasContent(res)) return mkfailure<value>(reason(res)); - // Return result - failable<list<value> > jsres = json::readJSON(cadr<list<string> >(content(res)), cx); - if (!hasContent(jsres)) - return mkfailure<value>(reason(jsres)); - const list<value> val = elementsToValues(content(jsres)); - - const value rval(cadr<value>(cadr<value>(val))); - debug(rval, "http::evalExpr::result"); - return rval; + // Parse and return JSON-RPC result + const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx); + if (!hasContent(rval)) + return mkfailure<value>(reason(rval)); + debug(content(rval), "http::evalExpr::result"); + return content(rval); } /** diff --git a/sca-cpp/trunk/modules/json/json-test.cpp b/sca-cpp/trunk/modules/json/json-test.cpp index b4a6ba8746..41ac24a22e 100644 --- a/sca-cpp/trunk/modules/json/json-test.cpp +++ b/sca-cpp/trunk/modules/json/json-test.cpp @@ -54,17 +54,18 @@ bool testJSON() { const list<value> ac = mklist<value>(mklist<value>(element, "id", string("1234")), mklist<value>(attribute, "balance", 1000)); const list<value> cr = mklist<value>(mklist<value> (attribute, "name", string("jdoe")), cons<value>(element, cons<value>("address", ad)), cons<value>(element, cons<value>("account", ac))); const list<value> c = mklist<value>(cons<value>(element, cons<value>("customer", cr))); + ostringstream os; writeJSON<ostream*>(jsonWriter, &os, c, cx); - assert(str(os) == "{\"customer\":{\"name\":\"jdoe\",\"address\":{\"city\":\"san francisco\",\"state\":\"ca\"},\"account\":{\"id\":\"1234\",\"balance\":1000}}}"); + assert(str(os) == "{\"customer\":{\"@name\":\"jdoe\",\"address\":{\"@city\":\"san francisco\",\"@state\":\"ca\"},\"account\":{\"id\":\"1234\",\"@balance\":1000}}}"); } { const list<value> phones = mklist<value> (string("408-1234"), string("650-1234")); - const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", string("test\ttab")), mklist<value> (element, "firstName", string("test1"))); + const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", string("test\ttab")), mklist<value> (attribute, "firstName", string("test1"))); ostringstream os; writeJSON<ostream*>(jsonWriter, &os, l, cx); - assert(str(os) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"firstName\":\"test1\"}"); + assert(str(os) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"@firstName\":\"test1\"}"); istringstream is(str(os)); const list<string> il = streamList(is); @@ -75,6 +76,18 @@ bool testJSON() { write(content(writeJSON(r, cx)), wos); assert(str(wos) == str(os)); } + { + const list<value> l = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!"))); + cout << "l: " << l << endl; + ostringstream wos; + write(content(writeJSON(valuesToElements(l), cx)), wos); + assert(str(wos) == "{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}"); + + istringstream is(str(wos)); + const list<string> il = streamList(is); + const list<value> r = elementsToValues(content(readJSON(il, cx))); + assert(r == l); + } return true; } @@ -121,6 +134,30 @@ bool testJSONRPC() { write(content(writeJSON(e, cx)), os); assert(str(os) == f); } + { + const list<value> arg = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!"))); + const failable<list<string> > r = jsonRequest(1, "echo", mklist<value>(arg), cx); + ostringstream os; + write(content(r), os); + assert(str(os) == "{\"id\":1,\"method\":\"echo\",\"params\":[{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}]}"); + + istringstream is(str(os)); + const list<string> il = streamList(is); + const list<value> ir = elementsToValues(content(readJSON(il, cx))); + assert(car<value>(cadr<value>(caddr<value>(ir))) == arg); + } + { + const list<value> res = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples")) + (list<value>() + "text" + string("Hello World!"))); + const failable<list<string> > r = jsonResult(1, res, cx); + ostringstream os; + write(content(r), os); + assert(str(os) == "{\"id\":1,\"result\":{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/c/samples\",\"text\":\"Hello World!\"}}}"); + + istringstream is(str(os)); + const list<string> il = streamList(is); + const list<value> ir = elementsToValues(content(readJSON(il, cx))); + assert(cdr<value>(cadr<value>(ir)) == res); + } return true; } diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp index 1116511455..4c36b8b477 100644 --- a/sca-cpp/trunk/modules/json/json.hpp +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -131,6 +131,22 @@ private: }; /** + * Returns true if a list represents a JS array. + */ +const bool isJSArray(const list<value>& l) { + if(isNil(l)) + return true; + const value v = car(l); + if (isSymbol(v)) + return false; + if(isList(v)) { + if(isSymbol(car<value>(v))) + return false; + } + return true; +} + +/** * Converts JS properties to values. */ const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObject* o, JSObject* i, const JSONContext& cx) { @@ -144,11 +160,16 @@ const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObj if(!JS_GetPropertyById(cx, o, id, &jsv)) return propertiesSoFar; const value val = jsValToValue(jsv, cx); + jsval idv; JS_IdToValue(cx, id, &idv); if(JSVAL_IS_STRING(idv)) { - const value type = isList(val)? element : element; - return jsPropertiesToValues(cons<value> (mklist<value> (type, JS_GetStringBytes(JSVAL_TO_STRING(idv)), val), propertiesSoFar), o, i, cx); + const string name = JS_GetStringBytes(JSVAL_TO_STRING(idv)); + if (substr(name, 0, 1) == atsign) + return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, i, cx); + if (isList(val) && !isJSArray(val)) + return jsPropertiesToValues(cons<value>(cons<value>(element, cons<value>(c_str(name), list<value>(val))), propertiesSoFar), o, i, cx); + return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, i, cx); } return jsPropertiesToValues(cons(val, propertiesSoFar), o, i, cx); } @@ -227,39 +248,11 @@ JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSO } /** - * Returns true if a list represents a JS array. - */ -const bool isJSArray(const list<value>& l) { - if(isNil(l)) - return false; - const value v = car(l); - if(isList(v)) { - const list<value> p = v; - if(isSymbol(car(p))) - return false; - } - return true; -} - - - -/** - * Converts a list of values to JS properties. - */ -JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) { - const jsval valueToJSVal(const value& val, const JSONContext& cx); - if(isNil(l)) - return o; - const list<value> p = car(l); - jsval pv = valueToJSVal(caddr(p), cx); - JS_SetProperty(cx, o, c_str((string)cadr(p)), &pv); - return valuesToJSProperties(o, cdr(l), cx); -} - -/** * Converts a value to a JS val. */ const jsval valueToJSVal(const value& val, const JSONContext& cx) { + JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx); + switch(type(val)) { case value::String: case value::Symbol: { @@ -282,16 +275,19 @@ const jsval valueToJSVal(const value& val, const JSONContext& cx) { } } -const failable<bool> writeList(const list<value>& l, JSObject* o, const JSONContext& cx) { +/** + * Converts a list of values to JS properties. + */ +JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) { if (isNil(l)) - return true; + return o; // Write an attribute const value token(car(l)); if (isTaggedList(token, attribute)) { jsval pv = valueToJSVal(attributeValue(token), cx); - JS_SetProperty(cx, o, c_str(string(attributeName(token))), &pv); + JS_SetProperty(cx, o, c_str(atsign + string(attributeName(token))), &pv); } else if (isTaggedList(token, element)) { @@ -308,14 +304,12 @@ const failable<bool> writeList(const list<value>& l, JSObject* o, const JSONCont JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv); // Write its children - const failable<bool> w = writeList(elementChildren(token), child, cx); - if (!hasContent(w)) - return w; + valuesToJSProperties(child, elementChildren(token), cx); } } // Go on - return writeList(cdr(l), o, cx); + return valuesToJSProperties(o, cdr(l), cx); } /** @@ -344,11 +338,7 @@ template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *d * Convert a list of values to a JSON document. */ template<typename R> const failable<R> writeJSON(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) { - JSObject* o = JS_NewObject(cx, NULL, NULL, NULL); - jsval val = OBJECT_TO_JSVAL(o); - const failable<bool> w = writeList(l, o, cx); - if (!hasContent(w)) - return mkfailure<R>(reason(w)); + jsval val = OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), l, cx)); WriteContext<R> wcx(reduce, initial, cx); if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx)) @@ -367,7 +357,7 @@ const failable<list<string> > writeJSON(const list<value>& l, const JSONContext& } /** - * Convert a function + params to a JSON request. + * Convert a list of function + params to a JSON-RPC request. */ const failable<list<string> > jsonRequest(const value& id, const value& func, const value& params, json::JSONContext& cx) { const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("method", string(func)), mklist<value>("params", params)); @@ -375,13 +365,27 @@ const failable<list<string> > jsonRequest(const value& id, const value& func, co } /** - * Convert a value to a JSON result. + * Convert a value to a JSON-RPC result. */ const failable<list<string> > jsonResult(const value& id, const value& val, JSONContext& cx) { return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx); } /** + * Convert a JSON-RPC result to a value. + */ +const failable<value> jsonResultValue(const list<string>& s, JSONContext& cx) { + const failable<list<value> > jsres = json::readJSON(s, cx); + if (!hasContent(jsres)) + return mkfailure<value>(reason(jsres)); + const list<value> rval(cadr<value>(elementsToValues(content(jsres)))); + const value val = cadr(rval); + if (isList(val) && !isJSArray(val)) + return value(mklist<value>(val)); + return val; +} + +/** * Return a portable function name from a JSON-RPC function name. * Strip the "system." and "Service." prefixes added by some JSON-RPC clients. */ diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp index f5c4266cc1..dfc376c55c 100644 --- a/sca-cpp/trunk/modules/server/mod-eval.hpp +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -269,19 +269,45 @@ int handler(request_rec *r) { /** * Convert a list of component references to a list of HTTP proxy lambdas. */ -const value mkproxy(const value& ref, const string& base) { +const value mkrefProxy(const value& ref, const string& base) { return lambda<value(const list<value>&)>(http::proxy(base + string(scdl::name(ref)))); } -const list<value> proxies(const list<value>& refs, const string& base) { +const list<value> refProxies(const list<value>& refs, const string& base) { if (isNil(refs)) return refs; - return cons(mkproxy(car(refs), base), proxies(cdr(refs), base)); + return cons(mkrefProxy(car(refs), base), refProxies(cdr(refs), base)); } -extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px); +/** + * Convert a list of component properties to a list of lambda functions that just return + * the property value. + */ +struct propProxy { + const value v; + propProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + return v; + } +}; + +const value mkpropProxy(const value& prop) { + return lambda<value(const list<value>&)>(propProxy(elementValue(prop))); +} + +const list<value> propProxies(const list<value>& props) { + if (isNil(props)) + return props; + return cons(mkpropProxy(car(props)), propProxies(cdr(props))); +} + +/** + * Evaluate a component and convert it to an applicable lambda function. + */ +const value evalComponent(DirConf& dc, ServerConf& sc, server_rec& server, const value& comp) { + extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px); -const value confImplementation(DirConf& dc, ServerConf& sc, server_rec& server, const value& comp) { const value impl = scdl::implementation(comp); // Convert component references to configured proxy lambdas @@ -293,11 +319,13 @@ const value confImplementation(DirConf& dc, ServerConf& sc, server_rec& server, << "/references/" << string(scdl::name(comp)) << "/"; else base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; - const list<value> px(proxies(scdl::references(comp), str(base))); + const list<value> rpx(refProxies(scdl::references(comp), str(base))); + + // Convert component proxies to configured proxy lambdas + const list<value> ppx(propProxies(scdl::properties(comp))); - // Evaluate the component implementation and convert it to an - // applicable lambda function - const failable<lambda<value(const list<value>&)> > cimpl(evalImplementation(dc.contributionPath, impl, px)); + // Evaluate the component implementation and convert it to an applicable lambda function + const failable<lambda<value(const list<value>&)> > cimpl(evalImplementation(dc.contributionPath, impl, append(rpx, ppx))); if (!hasContent(cimpl)) return reason(cimpl); return content(cimpl); @@ -309,7 +337,7 @@ const value confImplementation(DirConf& dc, ServerConf& sc, server_rec& server, const list<value> componentToImplementationAssoc(DirConf& dc, ServerConf& sc, server_rec& server, const list<value>& c) { if (isNil(c)) return c; - return cons<value>(mklist<value>(scdl::name(car(c)), confImplementation(dc, sc, server, car(c))), componentToImplementationAssoc(dc, sc, server, cdr(c))); + return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(dc, sc, server, car(c))), componentToImplementationAssoc(dc, sc, server, cdr(c))); } const list<value> componentToImplementationTree(DirConf& dc, ServerConf& sc, server_rec& server, const list<value>& c) { |