Working Web service component using Axis2C 1.6. Some fixes to the JSON conversion functions to correctly handle all cases of nested objects and arrays. Added support for component properties, the Web service component has a URI property that can be configured to the address of the target Web service.

git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@902540 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
jsdelfino 2010-01-24 09:27:52 +00:00
commit 878131a50c
18 changed files with 565 additions and 96 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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;
}
}
}

View file

@ -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)
])

View file

@ -81,5 +81,6 @@ scdl-test
java-test
java-shell
script-test
webservice-test
axiom-test
axis2-test

View file

@ -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)))

View file

@ -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;

View file

@ -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 */

View file

@ -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);
}
/**

View file

@ -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;
}

View file

@ -130,6 +130,22 @@ private:
JSObject* global;
};
/**
* 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.
*/
@ -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);
}
@ -226,40 +247,12 @@ JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSO
return valuesToJSElements(a, cdr(l), ++i, cx);
}
/**
* Returns true if a list represents a JS array.
*/
const bool isJSArray(const list<value>& l) {
if(isNil(l))
return false;
const value v = car(l);
if(isList(v)) {
const list<value> p = v;
if(isSymbol(car(p)))
return false;
}
return true;
}
/**
* Converts a list of values to JS properties.
*/
JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) {
const jsval valueToJSVal(const value& val, const JSONContext& cx);
if(isNil(l))
return o;
const list<value> p = car(l);
jsval pv = valueToJSVal(caddr(p), cx);
JS_SetProperty(cx, o, 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,12 +365,26 @@ 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.

View file

@ -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)));
// 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));
// 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, 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) {