summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sca-cpp/trunk/components/webservice/Makefile.am13
-rw-r--r--sca-cpp/trunk/components/webservice/axiom-test.cpp85
-rw-r--r--sca-cpp/trunk/components/webservice/axis2-test.cpp (renamed from sca-cpp/trunk/components/webservice/webservice-test.cpp)22
-rw-r--r--sca-cpp/trunk/components/webservice/client-test.cpp19
-rwxr-xr-xsca-cpp/trunk/components/webservice/echo-test35
-rwxr-xr-xsca-cpp/trunk/components/webservice/server-test49
-rw-r--r--sca-cpp/trunk/components/webservice/webservice.composite1
-rw-r--r--sca-cpp/trunk/components/webservice/webservice.cpp16
-rw-r--r--sca-cpp/trunk/components/webservice/webservice.hpp150
-rw-r--r--sca-cpp/trunk/configure.ac4
-rw-r--r--sca-cpp/trunk/etc/git-exclude3
-rw-r--r--sca-cpp/trunk/kernel/element.hpp10
-rw-r--r--sca-cpp/trunk/kernel/xml-test.cpp20
-rw-r--r--sca-cpp/trunk/kernel/xml.hpp30
-rw-r--r--sca-cpp/trunk/modules/http/curl.hpp17
-rw-r--r--sca-cpp/trunk/modules/json/json-test.cpp43
-rw-r--r--sca-cpp/trunk/modules/json/json.hpp96
-rw-r--r--sca-cpp/trunk/modules/server/mod-eval.hpp48
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) {