diff options
Diffstat (limited to 'sca-cpp/branches/gcc-4.4/modules')
131 files changed, 12386 insertions, 0 deletions
diff --git a/sca-cpp/branches/gcc-4.4/modules/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/Makefile.am new file mode 100644 index 0000000000..5ce2787fcf --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/Makefile.am @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +SUBDIRS = scheme atom json scdl http server python wsgi java + +includedir = $(prefix)/include/modules +nobase_include_HEADERS = */*.hpp + diff --git a/sca-cpp/branches/gcc-4.4/modules/atom/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/atom/Makefile.am new file mode 100644 index 0000000000..6eccee83c7 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/atom/Makefile.am @@ -0,0 +1,22 @@ +# 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. + +atom_test_SOURCES = atom-test.cpp +atom_test_LDFLAGS = -lxml2 + +noinst_PROGRAMS = atom-test +TESTS = atom-test diff --git a/sca-cpp/branches/gcc-4.4/modules/atom/atom-test.cpp b/sca-cpp/branches/gcc-4.4/modules/atom/atom-test.cpp new file mode 100644 index 0000000000..316e8b44db --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/atom/atom-test.cpp @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test ATOM data conversion functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "atom.hpp" + +namespace tuscany { +namespace atom { + +ostream* writer(const string& s, ostream* os) { + (*os) << s; + return os; +} + +string itemEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Apple</name><price>$2.99</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>" + "</entry>\n"); + +string incompleteEntry("<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title>item</title><content type=\"text/xml\">" + "<Item xmlns=\"http://services/\">" + "<name xmlns=\"\">Orange</name>" + "<price xmlns=\"\">3.55</price>" + "</Item>" + "</content>" + "</entry>"); + +string completedEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id></id>" + "<content type=\"application/xml\">" + "<Item xmlns=\"http://services/\">" + "<name xmlns=\"\">Orange</name>" + "<price xmlns=\"\">3.55</price>" + "</Item>" + "</content><link href=\"\"/>" + "</entry>\n"); + +bool testEntry() { + { + const list<value> i = list<value>() + element + value("item") + + value(list<value>() + element + value("name") + value(string("Apple"))) + + value(list<value>() + element + value("price") + value(string("$2.99"))); + const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + ostringstream os; + writeATOMEntry<ostream*>(writer, &os, a); + assert(str(os) == itemEntry); + } + { + const list<value> a = content(readATOMEntry(mklist(itemEntry))); + ostringstream os; + writeATOMEntry<ostream*>(writer, &os, a); + assert(str(os) == itemEntry); + } + { + const list<value> a = content(readATOMEntry(mklist(incompleteEntry))); + ostringstream os; + writeATOMEntry<ostream*>(writer, &os, a); + assert(str(os) == completedEntry); + } + return true; +} + +string emptyFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">Feed</title>" + "<id>1234</id>" + "</feed>\n"); + +string itemFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">Feed</title>" + "<id>1234</id>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Apple</name><price>$2.99</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>" + "</entry>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Orange</name><price>$3.55</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c\"/>" + "</entry>" + "</feed>\n"); + +bool testFeed() { + { + ostringstream os; + writeATOMFeed<ostream*>(writer, &os, mklist<value>("Feed", "1234")); + assert(str(os) == emptyFeed); + } + { + const list<value> a = content(readATOMFeed(mklist(emptyFeed))); + ostringstream os; + writeATOMFeed<ostream*>(writer, &os, a); + assert(str(os) == emptyFeed); + } + { + const list<value> i = list<value>() + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + + (list<value>() + element + "item" + + (list<value>() + element + "name" + "Apple") + + (list<value>() + element + "price" + "$2.99"))) + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + + (list<value>() + element + "item" + + (list<value>() + element + "name" + "Orange") + + (list<value>() + element + "price" + "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("1234", i)); + ostringstream os; + writeATOMFeed<ostream*>(writer, &os, a); + assert(str(os) == itemFeed); + } + { + const list<value> i = list<value>() + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + + valueToElement(list<value>() + "item" + + (list<value>() + "name" + "Apple") + + (list<value>() + "price" + "$2.99"))) + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + + valueToElement(list<value>() + "item" + + (list<value>() + "name" + "Orange") + + (list<value>() + "price" + "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("1234", i)); + ostringstream os; + writeATOMFeed<ostream*>(writer, &os, a); + assert(str(os) == itemFeed); + } + { + const list<value> a = content(readATOMFeed(mklist(itemFeed))); + ostringstream os; + writeATOMFeed<ostream*>(writer, &os, a); + assert(str(os) == itemFeed); + } + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::atom::testEntry(); + tuscany::atom::testFeed(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/atom/atom.hpp b/sca-cpp/branches/gcc-4.4/modules/atom/atom.hpp new file mode 100644 index 0000000000..fadb482e0d --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/atom/atom.hpp @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_atom_hpp +#define tuscany_atom_hpp + +/** + * ATOM data conversion functions. + */ + +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "xml.hpp" + +namespace tuscany { +namespace atom { + +/** + * Convert a list of elements to a list of values representing an ATOM entry. + */ +const list<value> entryElementsToValues(const list<value>& e) { + const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e); + const value t = isNil(lt)? value(emptyString) : elementValue(car(lt)); + const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e); + const value i = isNil(li)? value(emptyString) : elementValue(car(li)); + const list<value> lc = filter<value>(selector(mklist<value>(element, "content")), e); + return mklist<value>(t, i, cadr(elementChildren(car(lc)))); +} + +/** + * Convert a list of elements to a list of values representing ATOM entries. + */ +const list<value> entriesElementsToValues(const list<value>& e) { + if (isNil(e)) + return e; + return cons<value>(entryElementsToValues(car(e)), entriesElementsToValues(cdr(e))); +} + +/** + * Convert a list of strings to a list of values representing an ATOM entry. + */ +const failable<list<value> > readATOMEntry(const list<string>& ilist) { + const list<value> e = readXML(ilist); + if (isNil(e)) + return mkfailure<list<value> >("Empty entry"); + return entryElementsToValues(car(e)); +} + +/** + * Convert a list of values representing an ATOM entry to a value. + */ +const value entryValue(const list<value>& e) { + const list<value> v = elementsToValues(mklist<value>(caddr(e))); + return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v)))); +} + +/** + * Convert a list of strings to a list of values representing an ATOM feed. + */ +const failable<list<value> > readATOMFeed(const list<string>& ilist) { + const list<value> f = readXML(ilist); + if (isNil(f)) + return mkfailure<list<value> >("Empty feed"); + const list<value> t = filter<value>(selector(mklist<value>(element, "title")), car(f)); + const list<value> i = filter<value>(selector(mklist<value>(element, "id")), car(f)); + const list<value> e = filter<value>(selector(mklist<value>(element, "entry")), car(f)); + if (isNil(e)) + return mklist<value>(elementValue(car(t)), elementValue(car(i))); + return cons<value>(elementValue(car(t)), cons(elementValue(car(i)), entriesElementsToValues(e))); +} + +/** + * Convert a list of values representing an ATOM entry to a list of elements. + * The first two values in the list are the entry title and id. + */ +const list<value> entryElement(const list<value>& l) { + return list<value>() + + element + "entry" + (list<value>() + attribute + "xmlns" + "http://www.w3.org/2005/Atom") + + (list<value>() + element + "title" + (list<value>() + attribute + "type" + "text") + car(l)) + + (list<value>() + element + "id" + cadr(l)) + + (list<value>() + element + "content" + (list<value>() + attribute + "type" + "application/xml") + caddr(l)) + + (list<value>() + element + "link" + (list<value>() + attribute + "href" + cadr(l))); +} + +/** + * Convert a list of values representing ATOM entries to a list of elements. + */ +const list<value> entriesElements(const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(entryElement(car(l)), entriesElements(cdr(l))); +} + +/** + * Convert a list of values representing an ATOM entry to an ATOM entry. + * The first two values in the list are the entry id and title. + */ +template<typename R> const failable<R> writeATOMEntry(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + return writeXML<R>(reduce, initial, mklist<value>(entryElement(l))); +} + +const failable<list<string> > writeATOMEntry(const list<value>& l) { + const failable<list<string> > ls = writeATOMEntry<list<string> >(rcons<string>, list<string>(), l); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); +} + +/** + * Convert a list of values representing an ATOM feed to an ATOM feed. + * The first two values in the list are the feed id and title. + */ +template<typename R> const failable<R> writeATOMFeed(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + const list<value> f = list<value>() + + element + "feed" + (list<value>() + attribute + "xmlns" + "http://www.w3.org/2005/Atom") + + (list<value>() + element + "title" + (list<value>() + attribute + "type" + "text") + car(l)) + + (list<value>() + element + "id" + cadr(l)); + if (isNil(cddr(l))) + return writeXML<R>(reduce, initial, mklist<value>(f)); + const list<value> fe = append(f, entriesElements(cddr(l))); + return writeXML<R>(reduce, initial, mklist<value>(fe)); +} + +/** + * Convert a list of values representing an ATOM feed to a list of strings. + * The first two values in the list are the feed id and title. + */ +const failable<list<string> > writeATOMFeed(const list<value>& l) { + const failable<list<string> > ls = writeATOMFeed<list<string>>(rcons<string>, list<string>(), l); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); +} + +/** + * Convert an ATOM entry containing a value to an ATOM entry containing an item element. + */ +const list<value> entryValuesToElements(const list<value> val) { + return cons(car(val), cons(cadr(val), valuesToElements(mklist<value>(cons<value>("item", (list<value>)caddr(val)))))); +} + +/** + * Convert an ATOM feed containing values to an ATOM feed containing elements. + */ +const list<value> feedValuesToElementsLoop(const list<value> val) { + if (isNil(val)) + return val; + return cons<value>(entryValuesToElements(car(val)), feedValuesToElementsLoop(cdr(val))); +} + +const list<value> feedValuesToElements(const list<value>& val) { + return cons(car<value>(val), cons<value>(cadr<value>(val), feedValuesToElementsLoop(cddr<value>(val)))); +} + +} +} + +#endif /* tuscany_atom_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/http/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/http/Makefile.am new file mode 100644 index 0000000000..180529620c --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/Makefile.am @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +INCLUDES = -I${HTTPD_INCLUDE} + +moddir=$(prefix)/modules/http +mod_SCRIPTS = httpd-conf httpd-start httpd-stop httpd-restart + +curl_test_SOURCES = curl-test.cpp +curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +prefix_DATA = httpd.prefix curl.prefix +prefixdir=$(prefix)/modules/http +httpd.prefix: $(top_builddir)/config.status + echo ${HTTPD_PREFIX} >httpd.prefix +curl.prefix: $(top_builddir)/config.status + echo ${CURL_PREFIX} >curl.prefix + +noinst_PROGRAMS = curl-test +TESTS = httpd-test http-test diff --git a/sca-cpp/branches/gcc-4.4/modules/http/conf/mime.types b/sca-cpp/branches/gcc-4.4/modules/http/conf/mime.types new file mode 100644 index 0000000000..4279f51bca --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/conf/mime.types @@ -0,0 +1,607 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This file controls what Internet media types are sent to the client for +# given file extension(s). Sending the correct media type to the client +# is important so they know how to handle the content of the file. +# Extra types can either be added here or by using an AddType directive +# in your config files. For more information about Internet media types, +# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type +# registry is at <http://www.iana.org/assignments/media-types/>. + +# MIME type Extensions +application/activemessage +application/andrew-inset ez +application/applefile +application/atom+xml atom +application/atomicmail +application/batch-smtp +application/beep+xml +application/cals-1840 +application/cnrp+xml +application/commonground +application/cpl+xml +application/cybercash +application/dca-rft +application/dec-dx +application/dvcs +application/edi-consent +application/edifact +application/edi-x12 +application/eshop +application/font-tdpfr +application/http +application/hyperstudio +application/iges +application/index +application/index.cmd +application/index.obj +application/index.response +application/index.vnd +application/iotp +application/ipp +application/isup +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/marc +application/mathematica +application/mathml+xml mathml +application/msword doc +application/news-message-id +application/news-transmission +application/ocsp-request +application/ocsp-response +application/octet-stream bin dms lha lzh exe class so dll dmg +application/oda oda +application/ogg ogg +application/parityfec +application/pdf pdf +application/pgp-encrypted +application/pgp-keys +application/pgp-signature +application/pkcs10 +application/pkcs7-mime +application/pkcs7-signature +application/pkix-cert +application/pkix-crl +application/pkixcmp +application/postscript ai eps ps +application/prs.alvestrand.titrax-sheet +application/prs.cww +application/prs.nprend +application/prs.plucker +application/qsig +application/rdf+xml rdf +application/reginfo+xml +application/remote-printing +application/riscos +application/rtf +application/sdp +application/set-payment +application/set-payment-initiation +application/set-registration +application/set-registration-initiation +application/sgml +application/sgml-open-catalog +application/sieve +application/slate +application/smil smi smil +application/srgs gram +application/srgs+xml grxml +application/timestamp-query +application/timestamp-reply +application/tve-trigger +application/vemmi +application/vnd.3gpp.pic-bw-large +application/vnd.3gpp.pic-bw-small +application/vnd.3gpp.pic-bw-var +application/vnd.3gpp.sms +application/vnd.3m.post-it-notes +application/vnd.accpac.simply.aso +application/vnd.accpac.simply.imp +application/vnd.acucobol +application/vnd.acucorp +application/vnd.adobe.xfdf +application/vnd.aether.imp +application/vnd.amiga.ami +application/vnd.anser-web-certificate-issue-initiation +application/vnd.anser-web-funds-transfer-initiation +application/vnd.audiograph +application/vnd.blueice.multipass +application/vnd.bmi +application/vnd.businessobjects +application/vnd.canon-cpdl +application/vnd.canon-lips +application/vnd.cinderella +application/vnd.claymore +application/vnd.commerce-battelle +application/vnd.commonspace +application/vnd.contact.cmsg +application/vnd.cosmocaller +application/vnd.criticaltools.wbs+xml +application/vnd.ctc-posml +application/vnd.cups-postscript +application/vnd.cups-raster +application/vnd.cups-raw +application/vnd.curl +application/vnd.cybank +application/vnd.data-vision.rdz +application/vnd.dna +application/vnd.dpgraph +application/vnd.dreamfactory +application/vnd.dxr +application/vnd.ecdis-update +application/vnd.ecowin.chart +application/vnd.ecowin.filerequest +application/vnd.ecowin.fileupdate +application/vnd.ecowin.series +application/vnd.ecowin.seriesrequest +application/vnd.ecowin.seriesupdate +application/vnd.enliven +application/vnd.epson.esf +application/vnd.epson.msf +application/vnd.epson.quickanime +application/vnd.epson.salt +application/vnd.epson.ssf +application/vnd.ericsson.quickcall +application/vnd.eudora.data +application/vnd.fdf +application/vnd.ffsns +application/vnd.fints +application/vnd.flographit +application/vnd.framemaker +application/vnd.fsc.weblaunch +application/vnd.fujitsu.oasys +application/vnd.fujitsu.oasys2 +application/vnd.fujitsu.oasys3 +application/vnd.fujitsu.oasysgp +application/vnd.fujitsu.oasysprs +application/vnd.fujixerox.ddd +application/vnd.fujixerox.docuworks +application/vnd.fujixerox.docuworks.binder +application/vnd.fut-misnet +application/vnd.grafeq +application/vnd.groove-account +application/vnd.groove-help +application/vnd.groove-identity-message +application/vnd.groove-injector +application/vnd.groove-tool-message +application/vnd.groove-tool-template +application/vnd.groove-vcard +application/vnd.hbci +application/vnd.hhe.lesson-player +application/vnd.hp-hpgl +application/vnd.hp-hpid +application/vnd.hp-hps +application/vnd.hp-pcl +application/vnd.hp-pclxl +application/vnd.httphone +application/vnd.hzn-3d-crossword +application/vnd.ibm.afplinedata +application/vnd.ibm.electronic-media +application/vnd.ibm.minipay +application/vnd.ibm.modcap +application/vnd.ibm.rights-management +application/vnd.ibm.secure-container +application/vnd.informix-visionary +application/vnd.intercon.formnet +application/vnd.intertrust.digibox +application/vnd.intertrust.nncp +application/vnd.intu.qbo +application/vnd.intu.qfx +application/vnd.irepository.package+xml +application/vnd.is-xpr +application/vnd.japannet-directory-service +application/vnd.japannet-jpnstore-wakeup +application/vnd.japannet-payment-wakeup +application/vnd.japannet-registration +application/vnd.japannet-registration-wakeup +application/vnd.japannet-setstore-wakeup +application/vnd.japannet-verification +application/vnd.japannet-verification-wakeup +application/vnd.jisp +application/vnd.kde.karbon +application/vnd.kde.kchart +application/vnd.kde.kformula +application/vnd.kde.kivio +application/vnd.kde.kontour +application/vnd.kde.kpresenter +application/vnd.kde.kspread +application/vnd.kde.kword +application/vnd.kenameaapp +application/vnd.koan +application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop +application/vnd.llamagraphics.life-balance.exchange+xml +application/vnd.lotus-1-2-3 +application/vnd.lotus-approach +application/vnd.lotus-freelance +application/vnd.lotus-notes +application/vnd.lotus-organizer +application/vnd.lotus-screencam +application/vnd.lotus-wordpro +application/vnd.mcd +application/vnd.mediastation.cdkey +application/vnd.meridian-slingshot +application/vnd.micrografx.flo +application/vnd.micrografx.igx +application/vnd.mif mif +application/vnd.minisoft-hp3000-save +application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf +application/vnd.mobius.dis +application/vnd.mobius.mbk +application/vnd.mobius.mqy +application/vnd.mobius.msl +application/vnd.mobius.plc +application/vnd.mobius.txf +application/vnd.mophun.application +application/vnd.mophun.certificate +application/vnd.motorola.flexsuite +application/vnd.motorola.flexsuite.adsi +application/vnd.motorola.flexsuite.fis +application/vnd.motorola.flexsuite.gotap +application/vnd.motorola.flexsuite.kmr +application/vnd.motorola.flexsuite.ttc +application/vnd.motorola.flexsuite.wem +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry +application/vnd.ms-asf +application/vnd.ms-excel xls +application/vnd.ms-lrm +application/vnd.ms-powerpoint ppt +application/vnd.ms-project +application/vnd.ms-tnef +application/vnd.ms-works +application/vnd.ms-wpl +application/vnd.mseq +application/vnd.msign +application/vnd.music-niff +application/vnd.musician +application/vnd.netfpx +application/vnd.noblenet-directory +application/vnd.noblenet-sealer +application/vnd.noblenet-web +application/vnd.novadigm.edm +application/vnd.novadigm.edx +application/vnd.novadigm.ext +application/vnd.obn +application/vnd.osa.netdeploy +application/vnd.palm +application/vnd.pg.format +application/vnd.pg.osasli +application/vnd.powerbuilder6 +application/vnd.powerbuilder6-s +application/vnd.powerbuilder7 +application/vnd.powerbuilder7-s +application/vnd.powerbuilder75 +application/vnd.powerbuilder75-s +application/vnd.previewsystems.box +application/vnd.publishare-delta-tree +application/vnd.pvi.ptid1 +application/vnd.pwg-multiplexed +application/vnd.pwg-xhtml-print+xml +application/vnd.quark.quarkxpress +application/vnd.rapid +application/vnd.s3sms +application/vnd.sealed.net +application/vnd.seemail +application/vnd.shana.informed.formdata +application/vnd.shana.informed.formtemplate +application/vnd.shana.informed.interchange +application/vnd.shana.informed.package +application/vnd.smaf +application/vnd.sss-cod +application/vnd.sss-dtf +application/vnd.sss-ntf +application/vnd.street-stream +application/vnd.svd +application/vnd.swiftview-ics +application/vnd.triscape.mxs +application/vnd.trueapp +application/vnd.truedoc +application/vnd.ufdl +application/vnd.uplanet.alert +application/vnd.uplanet.alert-wbxml +application/vnd.uplanet.bearer-choice +application/vnd.uplanet.bearer-choice-wbxml +application/vnd.uplanet.cacheop +application/vnd.uplanet.cacheop-wbxml +application/vnd.uplanet.channel +application/vnd.uplanet.channel-wbxml +application/vnd.uplanet.list +application/vnd.uplanet.list-wbxml +application/vnd.uplanet.listcmd +application/vnd.uplanet.listcmd-wbxml +application/vnd.uplanet.signal +application/vnd.vcx +application/vnd.vectorworks +application/vnd.vidsoft.vidconference +application/vnd.visio +application/vnd.visionary +application/vnd.vividence.scriptfile +application/vnd.vsf +application/vnd.wap.sic +application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo +application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf +application/vnd.wv.csp+wbxml +application/vnd.xara +application/vnd.xfdl +application/vnd.yamaha.hv-dic +application/vnd.yamaha.hv-script +application/vnd.yamaha.hv-voice +application/vnd.yellowriver-custom-menu +application/voicexml+xml vxml +application/watcherinfo+xml +application/whoispp-query +application/whoispp-response +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-chess-pgn pgn +application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-futuresplash spl +application/x-gtar gtar +application/x-gzip +application/x-hdf hdf +application/x-javascript js +application/x-koan skp skd skt skm +application/x-latex latex +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/x400-bp +application/xhtml+xml xhtml xht +application/xslt+xml xslt +application/xml xml xsl +application/xml-dtd dtd +application/xml-external-parsed-entity +application/zip zip +audio/32kadpcm +audio/amr +audio/amr-wb +audio/basic au snd +audio/cn +audio/dat12 +audio/dsr-es201108 +audio/dvi4 +audio/evrc +audio/evrc0 +audio/g722 +audio/g.722.1 +audio/g723 +audio/g726-16 +audio/g726-24 +audio/g726-32 +audio/g726-40 +audio/g728 +audio/g729 +audio/g729D +audio/g729E +audio/gsm +audio/gsm-efr +audio/l8 +audio/l16 +audio/l20 +audio/l24 +audio/lpc +audio/midi mid midi kar +audio/mpa +audio/mpa-robust +audio/mp4a-latm +audio/mpeg mpga mp2 mp3 +audio/parityfec +audio/pcma +audio/pcmu +audio/prs.sid +audio/qcelp +audio/red +audio/smv +audio/smv0 +audio/telephone-event +audio/tone +audio/vdvi +audio/vnd.3gpp.iufp +audio/vnd.cisco.nse +audio/vnd.cns.anp1 +audio/vnd.cns.inf1 +audio/vnd.digital-winds +audio/vnd.everad.plj +audio/vnd.lucent.voice +audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 +audio/vnd.nuera.ecelp7470 +audio/vnd.nuera.ecelp9600 +audio/vnd.octel.sbc +audio/vnd.qcelp +audio/vnd.rhetorex.32kadpcm +audio/vnd.vmx.cvsd +audio/x-aiff aif aiff aifc +audio/x-alaw-basic +audio/x-mpegurl m3u +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin +application/vnd.rn-realmedia rm +audio/x-wav wav +chemical/x-pdb pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +image/g3fax +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/naplps +image/png png +image/prs.btif +image/prs.pti +image/svg+xml svg +image/t38 +image/tiff tiff tif +image/tiff-fx +image/vnd.cns.inf2 +image/vnd.djvu djvu djv +image/vnd.dwg +image/vnd.dxf +image/vnd.fastbidsheet +image/vnd.fpx +image/vnd.fst +image/vnd.fujixerox.edmics-mmr +image/vnd.fujixerox.edmics-rlc +image/vnd.globalgraphics.pgb +image/vnd.mix +image/vnd.ms-modi +image/vnd.net-fpx +image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff +image/x-cmu-raster ras +image/x-icon ico +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/delivery-status +message/disposition-notification +message/external-body +message/http +message/news +message/partial +message/rfc822 +message/s-http +message/sip +message/sipfrag +model/iges igs iges +model/mesh msh mesh silo +model/vnd.dwf +model/vnd.flatland.3dml +model/vnd.gdl +model/vnd.gs-gdl +model/vnd.gtw +model/vnd.mts +model/vnd.parasolid.transmit.binary +model/vnd.parasolid.transmit.text +model/vnd.vtu +model/vrml wrl vrml +multipart/alternative +multipart/appledouble +multipart/byteranges +multipart/digest +multipart/encrypted +multipart/form-data +multipart/header-set +multipart/mixed +multipart/parallel +multipart/related +multipart/report +multipart/signed +multipart/voice-message +text/calendar ics ifb +text/css css +text/directory +text/enriched +text/html html htm +text/parityfec +text/plain asc txt +text/prs.lines.tag +text/rfc822-headers +text/richtext rtx +text/rtf rtf +text/sgml sgml sgm +text/t140 +text/tab-separated-values tsv +text/uri-list +text/vnd.abc +text/vnd.curl +text/vnd.dmclientscript +text/vnd.fly +text/vnd.fmi.flexstor +text/vnd.in3d.3dml +text/vnd.in3d.spot +text/vnd.iptc.nitf +text/vnd.iptc.newsml +text/vnd.latex-z +text/vnd.motorola.reflex +text/vnd.ms-mediapackage +text/vnd.net2phone.commcenter.command +text/vnd.sun.j2me.app-descriptor +text/vnd.wap.si +text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-setext etx +text/xml +text/xml-external-parsed-entity +video/bmpeg +video/bt656 +video/celb +video/dv +video/h261 +video/h263 +video/h263-1998 +video/h263-2000 +video/jpeg +video/mp1s +video/mp2p +video/mp2t +video/mp4v-es +video/mpv +video/mpeg mpeg mpg mpe +video/nv +video/parityfec +video/pointer +video/quicktime qt mov +video/smpte292m +video/vnd.fvt +video/vnd.motorola.video +video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.nokia.interleaved-multimedia +video/vnd.objectvideo +video/vnd.vivo +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/sca-cpp/branches/gcc-4.4/modules/http/curl-test.cpp b/sca-cpp/branches/gcc-4.4/modules/http/curl-test.cpp new file mode 100644 index 0000000000..c9b85ad962 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/curl-test.cpp @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "perf.hpp" +#include "curl.hpp" + +namespace tuscany { +namespace http { + +string testURI = "http://localhost:8090"; + +ostream* curlWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +const bool testGet() { + CURLSession ch; + { + ostringstream os; + const failable<list<ostream*> > r = get<ostream*>(curlWriter, &os, testURI, ch); + assert(hasContent(r)); + assert(contains(str(os), "HTTP/1.1 200 OK")); + assert(contains(str(os), "It works")); + } + { + const failable<value> r = getcontent(testURI, ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + } + return true; +} + +struct getLoop { + CURLSession& ch; + getLoop(CURLSession& ch) : ch(ch) { + } + const bool operator()() const { + const failable<value> r = getcontent(testURI, ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + return true; + } +}; + +const bool testGetPerf() { + CURLSession ch; + lambda<bool()> gl = getLoop(ch); + cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl; + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::http::testURI = tuscany::string("http://") + tuscany::http::hostname() + ":8090"; + + tuscany::http::testGet(); + tuscany::http::testGetPerf(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/http/curl.hpp b/sca-cpp/branches/gcc-4.4/modules/http/curl.hpp new file mode 100644 index 0000000000..95c81d9b94 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/curl.hpp @@ -0,0 +1,441 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_curl_hpp +#define tuscany_curl_hpp + +/** + * CURL HTTP client functions. + */ + +#include <curl/curl.h> +#include <curl/types.h> +#include <curl/easy.h> +#include "string.hpp" +#include "gc.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "parallel.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" + +namespace tuscany { +namespace http { + +/** + * CURL library runtime, one per process. + */ +class CURLRuntime { +public: + CURLRuntime() { + curl_global_init(CURL_GLOBAL_ALL); + } +} curlRuntime; + +/** + * Represents a CURL session handle. + */ +class CURLSession { +public: + CURLSession(const string& ca = "", const string& cert = "", const string& key = "") : h(curl_easy_init()), owner(true), ca(ca), cert(cert), key(key) { + } + + CURLSession(const CURLSession& c) : h(c.h), owner(false), ca(c.ca), cert(c.cert), key(c.key) { + } + + ~CURLSession() { + if (!owner) + return; + if (h == NULL) + return; + curl_easy_cleanup(h); + } + +private: + CURL* h; + const bool owner; + + friend CURL* handle(const CURLSession& c); + +public: + const string ca; + const string cert; + const string key; +}; + +/** + * Returns the CURL handle used by a CURL session. + */ +CURL* handle(const CURLSession& c) { + return c.h; +} + +/** + * Context passed to the read callback function. + */ +class CURLReadContext { +public: + CURLReadContext(const list<string>& ilist) : ilist(ilist) { + } + list<string> ilist; +}; + +/** + * Called by CURL to read data to send. + */ +size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLReadContext& rcx = *static_cast<CURLReadContext*>(data); + if (isNil(rcx.ilist)) + return 0; + const list<string> f(fragment(rcx.ilist, size * nmemb)); + const string s = car(f); + rcx.ilist = cdr(f); + memcpy(ptr, c_str(s), length(s)); + return length(s); +} + +/** + * Context passed to CURL write callback function. + */ +template<typename R> class CURLWriteContext { +public: + CURLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { + } + const lambda<R(const string&, const R)> reduce; + R accum; +}; + +/** + * Called by CURL to write received data. + */ +template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); + const size_t realsize = size * nmemb; + wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum); + return realsize; +} + +/** + * Apply an HTTP verb to a list containing a list of headers and a list of content, and + * a reduce function used to process the response. + */ +curl_slist* headers(curl_slist* cl, const list<string>& h) { + if (isNil(h)) + return cl; + return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h)); +} + +template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) { + + // Init the curl session + CURL* ch = handle(cs); + curl_easy_reset(ch); + curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); + + //TODO use HTTP chunking, for now just convert request to a single string + ostringstream os; + write(cadr(hdr), os); + const string s = str(os); + const int sz = length(s); + + // Setup the read, header and write callbacks + CURLReadContext rcx(mklist(s)); + curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback); + curl_easy_setopt(ch, CURLOPT_READDATA, &rcx); + CURLWriteContext<R> hcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>)); + curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx); + CURLWriteContext<R> wcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>)); + curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx); + + // Setup protocol options + curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true); + curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true); + curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + + // Setup SSL options + if (cs.ca != "") { + debug(cs.ca, "http::apply::ca"); + curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca)); + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true); + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2); + } + if (cs.cert != "") { + debug(cs.cert, "http::apply::cert"); + curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert)); + curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); + } + if (cs.key != "") { + debug(cs.key, "http::apply::key"); + curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key)); + curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); + } + + // Set the request headers + curl_slist* hl = headers(NULL, car(hdr)); + if (hl != NULL) + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + + // Apply the HTTP verb + curl_easy_setopt(ch, CURLOPT_URL, c_str(url)); + if (verb == "POST") { + curl_easy_setopt(ch, CURLOPT_POST, true); + curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz); + } else if (verb == "PUT") { + curl_easy_setopt(ch, CURLOPT_UPLOAD, true); + curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz); + } else if (verb == "DELETE") + curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + const CURLcode rc = curl_easy_perform(ch); + + if (hl != NULL) + curl_slist_free_all(hl); + + // Return the HTTP return code or content + if (rc) + return mkfailure<list<R> >(string(curl_easy_strerror(rc))); + long httprc; + curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); + if (httprc != 200 && httprc != 201) { + ostringstream es; + es << "HTTP code " << httprc; + return mkfailure<list<R> >(str(es)); + } + return mklist<R>(hcx.accum, wcx.accum); +} + +/** + * Evaluate an expression remotely, at the given URL. + */ +const failable<value> evalExpr(const value& expr, const string& url, const CURLSession& ch) { + debug(url, "http::evalExpr::url"); + debug(expr, "http::evalExpr::input"); + + // Convert expression to a JSON-RPC request + json::JSONContext cx; + const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx); + if (!hasContent(jsreq)) + return mkfailure<value>(reason(jsreq)); + + // POST it to the URL + const list<string> h = mklist<string>("Content-Type: application/json-rpc"); + const failable<list<list<string> > > res = apply<list<string> >(mklist<list<string> >(h, content(jsreq)), rcons<string>, list<string>(), url, "POST", ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + + // 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); +} + +/** + * Find and return a header. + */ +const failable<string> header(const char* prefix, const list<string>& h) { + if (isNil(h)) + return mkfailure<string>(string("Couldn't find header: ") + prefix); + const string s = car(h); + if (find(s, prefix) != 0) + return header(prefix, cdr(h)); + const string l(substr(s, length(prefix))); + return substr(l, 0, find_first_of(l, "\r\n")); +} + +/** + * Find and return a location header. + */ +const failable<string> location(const list<string>& h) { + return header("Location: ", h); +} + +/** + * Convert a location to an entry id. + */ +const failable<value> entryId(const failable<string> l) { + if (!hasContent(l)) + return mkfailure<value>(reason(l)); + const string ls(content(l)); + return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1)))); +} + +/** + * Find and return a content-type header. + */ +const failable<string> contentType(const list<string>& h) { + return header("Content-Type: ", h); +} + +/** + * HTTP GET, return the resource at the given URL. + */ +template<typename R> const failable<list<R> > get(const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const CURLSession& ch) { + debug(url, "http::get::url"); + const list<list<string> > req = mklist(list<string>(), list<string>()); + return apply(req, reduce, initial, url, "GET", ch); +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value> getcontent(const string& url, const CURLSession& ch) { + debug(url, "http::get::url"); + + // Get the contents of the resource at the given URL + const failable<list<list<string> > > res = get<list<string>>(rcons<string>, list<string>(), url, ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + const list<string> ls(reverse(cadr(content(res)))); + + // Return the content as a list of values + const value val(mkvalues(ls)); + debug(val, "http::get::result"); + return val; +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value> get(const string& url, const CURLSession& ch) { + debug(url, "http::get::url"); + + // Get the contents of the resource at the given URL + const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + const list<string> ls(reverse(cadr(content(res)))); + + const string ct(content(contentType(car(content(res))))); + if (ct == "application/atom+xml;type=entry") { + const value val(atom::entryValue(content(atom::readATOMEntry(ls)))); + debug(val, "http::get::result"); + return val; + } + + // Return the content as a list of values + const value val(mkvalues(ls)); + debug(val, "http::get::result"); + return val; +} + +/** + * HTTP POST. + */ +const failable<value> post(const value& val, const string& url, const CURLSession& ch) { + + // Convert value to an ATOM entry + const failable<list<string> > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasContent(entry)) + return mkfailure<value>(reason(entry)); + debug(url, "http::post::url"); + debug(content(entry), "http::post::input"); + + // POST it to the URL + const list<string> h = mklist<string>("Content-Type: application/atom+xml"); + const list<list<string> > req = mklist<list<string> >(h, content(entry)); + const failable<list<list<string> > > res = apply<list<string>>(req, rcons<string>, list<string>(), url, "POST", ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + + // Return the new entry id from the HTTP location header + const failable<value> eid(entryId(location(car(content(res))))); + debug(eid, "http::post::result"); + return eid; +} + +/** + * HTTP PUT. + */ +const failable<value> put(const value& val, const string& url, const CURLSession& ch) { + + // Convert value to an ATOM entry + const failable<list<string> > entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasContent(entry)) + return mkfailure<value>(reason(entry)); + debug(url, "http::put::url"); + debug(content(entry), "http::put::input"); + + // PUT it to the URL + const list<string> h = mklist<string>("Content-Type: application/atom+xml"); + const list<list<string> > req = mklist<list<string> >(h, content(entry)); + const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "PUT", ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + + debug(true, "http::put::result"); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable<value, string> del(const string& url, const CURLSession& ch) { + debug(url, "http::delete::url"); + + const list<list<string> > req = mklist(list<string>(), list<string>()); + const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "DELETE", ch); + if (!hasContent(res)) + return mkfailure<value>(reason(res)); + + debug(true, "http::delete::result"); + return value(true); +} + +/** + * Returns the current host name. + */ +const string hostname() { + char h[256]; + if (gethostname(h, 256) == -1) + return "localhost"; + return h; +} + +/** + * HTTP client proxy function. + */ +struct proxy { + proxy(const string& uri, const string& ca, const string& cert, const string& key) : uri(uri), ca(ca), cert(cert), key(key) { + } + + const value operator()(const list<value>& args) const { + CURLSession cs(ca, cert, key); + failable<value> val = evalExpr(args, uri, cs); + if (!hasContent(val)) + return value(); + return content(val); + } + + const string uri; + const string ca; + const string cert; + const string key; +}; + +} +} + +#endif /* tuscany_curl_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/http/htdocs/index.html b/sca-cpp/branches/gcc-4.4/modules/http/htdocs/index.html new file mode 100644 index 0000000000..1bfb3e30c2 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/htdocs/index.html @@ -0,0 +1,21 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + +<html><body><h1>It works!</h1></body></html> + diff --git a/sca-cpp/branches/gcc-4.4/modules/http/http-test b/sca-cpp/branches/gcc-4.4/modules/http/http-test new file mode 100755 index 0000000000..0db47fe189 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/http-test @@ -0,0 +1,32 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +./httpd-conf tmp localhost 8090 htdocs +./httpd-start tmp +sleep 2 + +# Test +./curl-test +rc=$? + +# Cleanup +./httpd-stop tmp +sleep 2 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-ca-conf b/sca-cpp/branches/gcc-4.4/modules/http/httpd-ca-conf new file mode 100755 index 0000000000..c5a3f8e894 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-ca-conf @@ -0,0 +1,89 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a test certification authority certificate +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +host=$2 + +# Don't override existing certificate +if [ -f $root/conf/ca.crt ]; then + return 0 +fi + +# Generate openssl configuration +mkdir -p $root/conf +umask 0007 +cat >$root/conf/openssl-ca.conf <<EOF +[ req ] +default_bits = 1024 +encrypt_key = no +prompt = no +distinguished_name = req_distinguished_name +x509_extensions = v3_ca + +[ req_distinguished_name ] +C = US +ST = CA +L = San Francisco +O = Test Authority Organization +OU = Test Authority Unit +CN = $host +emailAddress = root@$host + +[ v3_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +basicConstraints = CA:true + +[ca] +default_ca = ca_default + +[ca_default] +certificate = $root/conf/ca.crt +private_key = $root/conf/ca.key +serial = $root/conf/ca-serial +database = $root/conf/ca-database +new_certs_dir = $root/conf +default_md = sha1 +email_in_dn = no +default_days = 365 +default_crl_days = 30 +policy = policy_any +copy_extensions = none + +[ policy_any ] +countryName = supplied +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +EOF + +rm -rf $root/conf/*.pem +rm -f $root/conf/ca-database +echo 1000 > $root/conf/ca-serial +touch $root/conf/ca-database + +# Generate the certification authority certificate +openssl req -new -x509 -config $root/conf/openssl-ca.conf -out $root/conf/ca.crt -keyout $root/conf/ca.key + diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-cert-conf b/sca-cpp/branches/gcc-4.4/modules/http/httpd-cert-conf new file mode 100755 index 0000000000..b6dc8ebd6f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-cert-conf @@ -0,0 +1,58 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a test certificate +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +host=$2 + +# Don't regenerate the certificate if it already exists +if [ -f $root/conf/server.crt ]; then + return 0 +fi + +# Generate openssl configuration +mkdir -p $root/conf +umask 0007 +cat >$root/conf/openssl-cert.conf <<EOF +[ req ] +default_bits = 1024 +encrypt_key = no +prompt = no +distinguished_name = req_distinguished_name + +[ req_distinguished_name ] +C = US +ST = CA +L = San Francisco +O = Test Organization +OU = Test Unit +CN = $host +emailAddress = root@$host +EOF + +# Generate a certificate request +openssl req -new -config $root/conf/openssl-cert.conf -out $root/conf/server-req.crt -keyout $root/conf/server.key + +# Generate a certificate, signed with our test certification authority certificate +openssl ca -batch -config $root/conf/openssl-ca.conf -out $root/conf/server.crt -infiles $root/conf/server-req.crt + +# Export it to PKCS12 format, that's the format Web browsers want to import +openssl pkcs12 -export -passout pass: -out $root/conf/server.p12 -inkey $root/conf/server.key -in $root/conf/server.crt -certfile $root/conf/ca.crt + diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-conf b/sca-cpp/branches/gcc-4.4/modules/http/httpd-conf new file mode 100755 index 0000000000..fa3ce09fc9 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-conf @@ -0,0 +1,93 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a minimal HTTPD configuration +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +host=$2 +port=$3 +htdocs=`readlink -f $4` +user=`id -un` +group=`id -gn` + +mkdir -p $root +mkdir -p $root/logs +mkdir -p $root/conf +cat >$root/conf/httpd.conf <<EOF +# Apache HTTPD server configuration + +# Set server name +ServerName $host + +# Basic security precautions +User $user +Group $group +ServerSignature Off +ServerTokens Prod +Timeout 45 +LimitRequestBody 1048576 +HostNameLookups Off + +# Logging +ErrorLog $root/logs/error_log +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined +CustomLog $root/logs/access_log combined +LogLevel warn + +# Configure Mime types +DefaultType text/plain +TypesConfig $here/conf/mime.types + +# Set document root +DocumentRoot $htdocs +DirectoryIndex index.html + +# Protect server files +<Directory /> +Options None +AllowOverride None +Order deny,allow +Deny from all +</Directory> +<FilesMatch "^\.ht"> +Order deny,allow +Deny from all +Satisfy Any +</FilesMatch> + +# Allow access to document root +<Directory "$htdocs"> +Options +SymLinksIfOwnerMatch +Allow from all +</Directory> + +# Allow access to service components +<Location /> +Options +SymLinksIfOwnerMatch +Allow from all +</Location> + +# Setup HTTP virtual host +Listen $port +<VirtualHost _default_:$port> + +</VirtualHost> + +EOF + diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-restart b/sca-cpp/branches/gcc-4.4/modules/http/httpd-restart new file mode 100755 index 0000000000..92f27eb0df --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-restart @@ -0,0 +1,25 @@ +#!/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. + +# Restart httpd server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +httpd_prefix=`cat $here/httpd.prefix` +$httpd_prefix/bin/apachectl -k graceful -d $root diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-ssl-conf b/sca-cpp/branches/gcc-4.4/modules/http/httpd-ssl-conf new file mode 100755 index 0000000000..6f763c6a66 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-ssl-conf @@ -0,0 +1,94 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a minimal HTTPD SSL configuration +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +host=$2 +sslport=$3 +htdocs=`readlink -f $4` +httpd_prefix=`cat $here/httpd.prefix` + +# Extract organization name from our CA certificate +org=`openssl x509 -noout -subject -nameopt multiline -in $root/conf/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'` + +# Generate HTTPD configuration +cat >>$root/conf/httpd.conf <<EOF +# Redirect all HTTP traffic to HTTPS +<Location /> +RewriteEngine on +RewriteCond %{SERVER_PORT} !^$sslport$ +RewriteRule .* https://%{SERVER_NAME}:$sslport%{REQUEST_URI} [R,L] +</Location> + +# Setup SSL support +AddType application/x-x509-ca-cert .crt +AddType application/x-pkcs7-crl .crl +SSLPassPhraseDialog builtin +SSLSessionCache "shmcb:$root/logs/ssl_scache(512000)" +SSLSessionCacheTimeout 300 +SSLMutex "file:$root/logs/ssl_mutex" +SSLRandomSeed startup builtin +SSLRandomSeed connect builtin + +# HTTPS virtual host +Listen $sslport +<VirtualHost _default_:$sslport> + +# Enable SSL +SSLEngine on +SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL +SSLCACertificateFile "$root/conf/ca.crt" +SSLCertificateFile "$root/conf/server.crt" +SSLCertificateKeyFile "$root/conf/server.key" +BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0 + +# Logging +CustomLog "$root/logs/ssl_request_log" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" +LogFormat "%h %l %u %t %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" sslcombined +CustomLog $root/logs/ssl_access_log sslcombined +LogLevel warn + +# Require clients to present either: +# a certificate signed with our certification authority certificate +# or a userid + password for HTTP basic authentication +<Location /> +Satisfy Any + +SSLVerifyClient optional +SSLVerifyDepth 1 +SSLOptions +FakeBasicAuth +SSLRequireSSL +SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 and %{SSL_CLIENT_I_DN_O} == "$org" + +AuthType Basic +AuthName "$host" +AuthUserFile "$root/conf/httpd.passwd" +Require valid-user +</location> + +</VirtualHost> + +EOF + +# Create test users for HTTP basic authentication +$httpd_prefix/bin/htpasswd -bc $root/conf/httpd.passwd test test 2>/dev/null +$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd foo foo 2>/dev/null +$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd bar bar 2>/dev/null + diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-start b/sca-cpp/branches/gcc-4.4/modules/http/httpd-start new file mode 100755 index 0000000000..91fc5284af --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-start @@ -0,0 +1,25 @@ +#!/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. + +# Start httpd server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +httpd_prefix=`cat $here/httpd.prefix` +$httpd_prefix/bin/apachectl -E $root/logs/error_log -k start -d $root diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-stop b/sca-cpp/branches/gcc-4.4/modules/http/httpd-stop new file mode 100755 index 0000000000..7f4fe94629 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-stop @@ -0,0 +1,25 @@ +#!/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. + +# Stop httpd server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +httpd_prefix=`cat $here/httpd.prefix` +$httpd_prefix/bin/apachectl -k graceful-stop -d $root diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd-test b/sca-cpp/branches/gcc-4.4/modules/http/httpd-test new file mode 100755 index 0000000000..a3b9145871 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd-test @@ -0,0 +1,40 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +./httpd-conf tmp localhost 8090 htdocs +./httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Cleanup +./httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/http/httpd.hpp b/sca-cpp/branches/gcc-4.4/modules/http/httpd.hpp new file mode 100644 index 0000000000..dfc3dcc47e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/http/httpd.hpp @@ -0,0 +1,404 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_httpd_hpp +#define tuscany_httpd_hpp + +/** + * HTTPD module implementation functions. + */ + +#include "apr_strings.h" +#include "apr_fnmatch.h" +#include "apr_lib.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "util_md5.h" +#include "http_config.h" +#include "ap_mpm.h" +#include "mod_core.h" + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "value.hpp" + + +namespace tuscany { +namespace httpd { + +/** + * Returns a server-scoped module configuration. + */ +template<typename C> void* makeServerConf(apr_pool_t *p, server_rec *s) { + return new (gc_new<C>(p)) C(s); +} + +template<typename C> const C& serverConf(const request_rec* r, const module* mod) { + return *(C*)ap_get_module_config(r->server->module_config, mod); +} + +template<typename C> C& serverConf(const server_rec* s, const module* mod) { + return *(C*)ap_get_module_config(s->module_config, mod); +} + +template<typename C> C& serverConf(const cmd_parms *cmd, const module* mod) { + return *(C*)ap_get_module_config(cmd->server->module_config, mod); +} + + +/** + * Return the content type of a request. + */ +const char* optional(const char* s) { + if (s == NULL) + return ""; + return s; +} + +const string contentType(const request_rec* r) { + return optional(apr_table_get(r->headers_in, "Content-Type")); +} + +#ifdef WANT_MAINTAINER_MODE + +/** + * Debug log. + */ +int debugHeader(unused void* r, const char* key, const char* value) { + cerr << " header key: " << key << ", value: " << value << endl; + return 1; +} + +const bool debugRequest(request_rec* r, const string& msg) { + cerr << msg << ":" << endl; + cerr << " protocol: " << optional(r->protocol) << endl; + cerr << " method: " << optional(r->method) << endl; + cerr << " method number: " << r->method_number << endl; + cerr << " content type: " << contentType(r) << endl; + cerr << " content encoding: " << optional(r->content_encoding) << endl; + apr_table_do(debugHeader, r, r->headers_in, NULL); + cerr << " unparsed uri: " << optional(r->unparsed_uri) << endl; + cerr << " uri: " << optional(r->uri) << endl; + cerr << " path info: " << optional(r->path_info) << endl; + cerr << " filename: " << optional(r->filename) << endl; + cerr << " uri tokens: " << pathTokens(r->uri) << endl; + cerr << " args: " << optional(r->args) << endl; + return true; +} + +#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg) + +#else + +#define httpdDebugRequest(r, msg) + +#endif + +/** + * Return the remaining part of a uri after the given path (aka the path info.) + */ +const list<value> pathInfo(const list<value>& uri, const list<value>& path) { + if (isNil(path)) + return uri; + return pathInfo(cdr(uri), cdr(path)); +} + +/** + * Returns a list of key value pairs from the args in a query string. + */ +const list<value> queryArg(const string& s) { + const list<string> t = tokenize("=", s); + return mklist<value>(c_str(car(t)), cadr(t)); +} + +const list<list<value> > queryArgs(const request_rec* r) { + const char* a = r->args; + if (a == NULL) + return list<list<value> >(); + return map<string, list<value>>(queryArg, tokenize("&", a)); +} + +/** + * Returns a list of param values other than the id and method args from a list + * of key value pairs. + */ +const list<value> queryParams(const list<list<value> >& a) { + if (isNil(a)) + return list<value>(); + const list<value> p = car(a); + if (car(p) == value("id") || car(p) == value("method")) + return queryParams(cdr(a)); + return cons(cadr(p), queryParams(cdr(a))); +} + +/** + * Converts the args received in a POST to a list of key value pairs. + */ +const list<list<value> > postArgs(const list<value>& a) { + if (isNil(a)) + return list<list<value> >(); + const list<value> l = car(a); + return cons(l, postArgs(cdr(a))); +} + +/** + * Setup the HTTP read policy. + */ +const int setupReadPolicy(request_rec* r) { + const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); + if(rc != OK) + return rc; + ap_should_client_block(r); + if(r->read_chunked == true && r->remaining == 0) + r->chunked = true; + //apr_table_setn(r->headers_out, "Connection", "close"); + return OK; +} + +/** + * Read the content of a POST or PUT. + */ +const list<string> read(request_rec* r) { + char b[1024]; + const int n = ap_get_client_block(r, b, sizeof(b)); + if (n <= 0) + return list<string>(); + return cons(string(b, n), read(r)); +} + +/** + * Convert a URI represented as a list to an absolute URL. + */ +const char* url(const list<value>& v, request_rec* r) { + const string u = string(r->uri) + path(v); + return ap_construct_url(r->pool, c_str(u), r); +} + +/** + * Write an HTTP result. + */ +const failable<int> writeResult(const failable<list<string> >& ls, const string& ct, request_rec* r) { + if (!hasContent(ls)) + return mkfailure<int>(reason(ls)); + ostringstream os; + write(content(ls), os); + const string ob(str(os)); + debug(ob, "httpd::result"); + + // Make sure browsers come back and check for updated dynamic content + apr_table_setn(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT"); + + // Compute and return an Etag for the returned content + const string etag(ap_md5(r->pool, (const unsigned char*)c_str(ob))); + + // Check for an If-None-Match header and just return a 304 not-modified status + // if the Etag matches the Etag presented by the client, to save bandwith + const char* match = apr_table_get(r->headers_in, "If-None-Match"); + apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, c_str(etag))); + if (match != NULL && etag == match) { + + r->status = HTTP_NOT_MODIFIED; + return OK; + } + ap_set_content_type(r, apr_pstrdup(r->pool, c_str(ct))); + ap_rputs(c_str(ob), r); + return OK; +} + +/** + * Report request execution status. + */ +const int reportStatus(const failable<int>& rc) { + if (!hasContent(rc)) + return HTTP_INTERNAL_SERVER_ERROR; + return content(rc); +} + +/** + * Construct a redirect URI. + */ +const string redirectURI(const string& file, const string& pi) { + return file + pi; +} + +const string redirectURI(const string& file, const string& pi, const string& args) { + return file + pi + "?" + args; +} + +/** + * Convert a value to an HTTPD request struc + */ +request_rec* request(const value& v) { + return (request_rec*)(long)(double)v; +} + +/** + * Convert an HTTPD request struct to a value + */ +const value requestValue(request_rec* r) { + return value((double)(long)r); +} + +/** + * Update filters in an HTTPD redirect request. + * Similar to httpd/modules/http/http_request.c::update_r_in_filters. + */ +const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) { + if (f == NULL) + return true; + if (f->r == from) + f->r = to; + return redirectFilters(f->next, from, to); +} + +/** + * Create an HTTPD redirect request. + * Similar to httpd/modules/http/http_request.c::internal_internal_redirect. + */ +extern "C" { + AP_DECLARE(ap_conf_vector_t*) ap_create_request_config(apr_pool_t *p); +} + +const failable<request_rec*, int> internalRedirectRequest(const string& nr_uri, request_rec* r) { + if (ap_is_recursion_limit_exceeded(r)) + return mkfailure<request_rec*, int>(HTTP_INTERNAL_SERVER_ERROR); + + // Create a new request + request_rec* nr = (request_rec*)apr_pcalloc(r->pool, sizeof(request_rec)); + nr->connection = r->connection; + nr->server = r->server; + nr->pool = r->pool; + nr->method = r->method; + nr->method_number = r->method_number; + nr->allowed_methods = ap_make_method_list(nr->pool, 2); + ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri))); + nr->request_config = ap_create_request_config(r->pool); + nr->per_dir_config = r->server->lookup_defaults; + nr->prev = r; + r->next = nr; + + // Run create request hook + ap_run_create_request(nr); + + // Inherit protocol info from the original request + nr->the_request = r->the_request; + nr->allowed = r->allowed; + nr->status = r->status; + nr->assbackwards = r->assbackwards; + nr->header_only = r->header_only; + nr->protocol = r->protocol; + nr->proto_num = r->proto_num; + nr->hostname = r->hostname; + nr->request_time = r->request_time; + nr->main = r->main; + nr->headers_in = r->headers_in; + nr->headers_out = apr_table_make(r->pool, 12); + nr->err_headers_out = r->err_headers_out; + nr->subprocess_env = r->subprocess_env; + nr->notes = apr_table_make(r->pool, 5); + nr->allowed_methods = ap_make_method_list(nr->pool, 2); + nr->htaccess = r->htaccess; + nr->no_cache = r->no_cache; + nr->expecting_100 = r->expecting_100; + nr->no_local_copy = r->no_local_copy; + nr->read_length = r->read_length; + nr->vlist_validator = r->vlist_validator; + + // Setup input and output filters + nr->proto_output_filters = r->proto_output_filters; + nr->proto_input_filters = r->proto_input_filters; + nr->output_filters = nr->proto_output_filters; + nr->input_filters = nr->proto_input_filters; + if (nr->main) + ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection); + redirectFilters(nr->input_filters, r, nr); + redirectFilters(nr->output_filters, r, nr); + const int rrc = ap_run_post_read_request(nr); + if (rrc != OK && rrc != DECLINED) + return mkfailure<request_rec*, int>(rrc); + + return nr; +} + +/** + * Process an HTTPD internal redirect request. + * Similar to httpd/modules/http/http_request.c::ap_internal_redirect. + */ +extern "C" { + AP_DECLARE(int) ap_invoke_handler(request_rec *r); +} + +const int internalRedirect(request_rec* nr) { + int status = ap_run_quick_handler(nr, 0); + if (status == DECLINED) { + status = ap_process_request_internal(nr); + if (status == OK) + status = ap_invoke_handler(nr); + } + if (status != OK) { + nr->status = status; + return OK; + } + ap_finalize_request_protocol(nr); + return OK; +} + +/** + * Create and process an HTTPD redirect request. + */ +const int internalRedirect(const string& uri, request_rec* r) { + const failable<request_rec*, int> nr = httpd::internalRedirectRequest(uri, r); + if (!hasContent(nr)) + return reason(nr); + return httpd::internalRedirect(content(nr)); +} + +/** + * Put a value in the process user data. + */ +const bool putUserData(const string& k, const void* v, const server_rec* s) { + apr_pool_userdata_set((const void *)v, c_str(k), apr_pool_cleanup_null, s->process->pool); + return true; +} + +/** + * Return a user data value. + */ +const void* userData(const string& k, const server_rec* s) { + void* v = NULL; + apr_pool_userdata_get(&v, c_str(k), s->process->pool); + return v; +} + +} +} + +#endif /* tuscany_httpd_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/java/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/java/Makefile.am new file mode 100644 index 0000000000..293b9431b5 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/Makefile.am @@ -0,0 +1,64 @@ +# 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. + +JAVAROOT = $(top_builddir)/modules/java +libdir = $(prefix)/modules/java + +if WANT_JAVA + +INCLUDES = -I${JAVA_INCLUDE} + +mod_SCRIPTS = java-conf +moddir = $(prefix)/modules/java + +prefix_DATA = java.prefix +prefixdir = $(prefix)/modules/java +java.prefix: $(top_builddir)/config.status + echo ${JAVA_PREFIX} >java.prefix + +lib_LTLIBRARIES = libmod_tuscany_java.la +libmod_tuscany_java_la_SOURCES = mod-java.cpp +libmod_tuscany_java_la_LDFLAGS = -lxml2 -lcurl -lmozjs ${JAVA_LDFLAGS} +noinst_DATA = libmod_tuscany_java.so +libmod_tuscany_java.so: + ln -s .libs/libmod_tuscany_java.so libmod_tuscany_java.so + +jni_test_SOURCES = jni-test.cpp +jni_test_LDFLAGS = ${JAVA_LDFLAGS} + +java_test_SOURCES = java-test.cpp +java_test_LDFLAGS = ${JAVA_LDFLAGS} + +java_shell_SOURCES = java-shell.cpp +java_shell_LDFLAGS = ${JAVA_LDFLAGS} + +noinst_JAVA = org/apache/tuscany/*.java test/*.java +jardir = ${prefix}/modules/java +jarfile = libmod-tuscany-java-${PACKAGE_VERSION}.jar +jar_DATA = ${jarfile} +${jarfile}: ${noinst_JAVA} + ${JAR} cf $@ org/apache/tuscany/*.class + +CLEANFILES = ${jarfile} org/apache/tuscany/*.class test/*.class + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +noinst_PROGRAMS = jni-test java-test java-shell client-test +TESTS = jni-test java-test server-test + +endif diff --git a/sca-cpp/branches/gcc-4.4/modules/java/client-test.cpp b/sca-cpp/branches/gcc-4.4/modules/java/client-test.cpp new file mode 100644 index 0000000000..d06c57721e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/client-test.cpp @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "../server/client-test.hpp" + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::server::testURI = "http://localhost:8090/java"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/domain-test.composite b/sca-cpp/branches/gcc-4.4/modules/java/domain-test.composite new file mode 100644 index 0000000000..190f2ff5bb --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/domain-test.composite @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="java-test"> + <implementation.java class="test.ServerImpl"/> + <service name="test"> + <t:binding.http uri="java"/> + </service> + </component> + + <component name="client-test"> + <implementation.java class="test.ClientImpl"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="java-test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/gcc-4.4/modules/java/driver.hpp b/sca-cpp/branches/gcc-4.4/modules/java/driver.hpp new file mode 100644 index 0000000000..ddfc057940 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/driver.hpp @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_java_driver_hpp +#define tuscany_java_driver_hpp + +/** + * Java evaluator main driver loop. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "monad.hpp" +#include "../scheme/driver.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace java { + +const value evalDriverLoop(const JavaRuntime& jr, const JavaClass jc, istream& in, ostream& out) { + scheme::promptForInput(scheme::evalInputPrompt, out); + value input = scheme::readValue(in); + if (isNil(input)) + return input; + const failable<value> output = evalClass(jr, input, jc); + scheme::announceOutput(scheme::evalOutputPrompt, out); + scheme::userPrint(content(output), out); + return evalDriverLoop(jr, jc, in, out); +} + +const bool evalDriverRun(const char* name, istream& in, ostream& out) { + scheme::setupDisplay(out); + JavaRuntime javaRuntime; + const failable<JavaClass> jc = readClass(javaRuntime, ".", name); + if (!hasContent(jc)) + return true; + evalDriverLoop(javaRuntime, content(jc), in, out); + return true; +} + +} +} +#endif /* tuscany_java_driver_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/java/eval.hpp b/sca-cpp/branches/gcc-4.4/modules/java/eval.hpp new file mode 100644 index 0000000000..a1cf6ae576 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/eval.hpp @@ -0,0 +1,567 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_java_eval_hpp +#define tuscany_java_eval_hpp + +/** + * Java component implementation evaluation logic. + */ +#include <jni.h> +#include <apr_uuid.h> + +#include "list.hpp" +#include "value.hpp" + +namespace tuscany { +namespace java { + +/** + * Handle differences between various JNI APIs. + */ +#ifdef JAVA_HARMONY_VM +#define JNI_VERSION JNI_VERSION_1_4 +#else +#define JNI_VERSION JNI_VERSION_1_6 +#endif + +/** + * Represent a Java VM runtime. + */ +jobject JNICALL nativeInvoke(JNIEnv *env, jobject self, jobject proxy, jobject method, jobjectArray args); +jobject JNICALL nativeUUID(JNIEnv *env); + +class JavaRuntime { +public: + JavaRuntime() { + + // Get existing JVM + jsize nvms = 0; + JNI_GetCreatedJavaVMs(&jvm, 1, &nvms); + if (nvms == 0) { + + // Create a new JVM + JavaVMInitArgs args; + args.version = JNI_VERSION; + args.ignoreUnrecognized = JNI_FALSE; + JavaVMOption options[3]; + args.options = options; + args.nOptions = 0; + + // Configure classpath + const char* envcp = getenv("CLASSPATH"); + const string cp = string("-Djava.class.path=") + (envcp == NULL? "." : envcp); + options[args.nOptions].optionString = const_cast<char*>(c_str(cp)); + options[args.nOptions++].extraInfo = NULL; + +#ifdef WANT_MAINTAINER_MODE + // Enable assertions + options[args.nOptions++].optionString = const_cast<char*>("-ea"); +#endif + + // Configure Java debugging + const char* jpdaopts = getenv("JPDA_OPTS"); + if (jpdaopts != NULL) { + options[args.nOptions].optionString = const_cast<char*>(jpdaopts); + options[args.nOptions++].extraInfo = NULL; + } else { + const char* jpdaaddr = getenv("JPDA_ADDRESS"); + if (jpdaaddr != NULL) { + const string jpda = string("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=") + jpdaaddr; + options[args.nOptions].optionString = const_cast<char*>(c_str(jpda)); + options[args.nOptions++].extraInfo = NULL; + } + } + + // Create the JVM +#ifdef JAVA_HARMONY_VM + JNI_CreateJavaVM(&jvm, &env, &args); +#else + JNI_CreateJavaVM(&jvm, (void**)&env, &args); +#endif + + } else { + + // Just point to existing JVM + jvm->GetEnv((void**)&env, JNI_VERSION); + } + + // Lookup System classes and methods + classClass = env->FindClass("java/lang/Class"); + methodClass = env->FindClass("java/lang/reflect/Method"); + objectClass = env->FindClass("java/lang/Object"); + doubleClass = env->FindClass("java/lang/Double"); + booleanClass = env->FindClass("java/lang/Boolean"); + stringClass = env->FindClass("java/lang/String"); + objectArrayClass = env->FindClass("[Ljava/lang/Object;"); + iterableClass = env->FindClass("java/lang/Iterable"); + classForName = env->GetStaticMethodID(classClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); + doubleValueOf = env->GetStaticMethodID(doubleClass, "valueOf", "(D)Ljava/lang/Double;"); + doubleValue = env->GetMethodID(doubleClass, "doubleValue", "()D"); + booleanValueOf = env->GetStaticMethodID(booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;"); + booleanValue = env->GetMethodID(booleanClass, "booleanValue", "()Z"); + declaredMethods = env->GetMethodID(classClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); + methodName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;"); + parameterTypes = env->GetMethodID(methodClass, "getParameterTypes", "()[Ljava/lang/Class;"); + + // Lookup Tuscany classes and methods + loaderClass = env->FindClass("org/apache/tuscany/ClassLoader"); + loaderValueOf = env->GetStaticMethodID(loaderClass, "valueOf", "(Ljava/lang/String;)Ljava/lang/ClassLoader;"); + loaderForName = env->GetStaticMethodID(loaderClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); + invokerClass = env->FindClass("org/apache/tuscany/InvocationHandler"); + invokerValueOf = env->GetStaticMethodID(invokerClass, "valueOf", "(Ljava/lang/Class;J)Ljava/lang/Object;"); + invokerStackTrace = env->GetStaticMethodID(invokerClass, "stackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;"); + invokerLambda = env->GetFieldID(invokerClass, "lambda", "J"); + iterableUtilClass = env->FindClass("org/apache/tuscany/IterableUtil"); + iterableValueOf = env->GetStaticMethodID(iterableUtilClass, "list", "([Ljava/lang/Object;)Ljava/lang/Iterable;"); + iterableIsNil = env->GetStaticMethodID(iterableUtilClass, "isNil", "(Ljava/lang/Object;)Z"); + iterableCar = env->GetStaticMethodID(iterableUtilClass, "car", "(Ljava/lang/Object;)Ljava/lang/Object;"); + iterableCdr = env->GetStaticMethodID(iterableUtilClass, "cdr", "(Ljava/lang/Object;)Ljava/lang/Iterable;"); + uuidClass = env->FindClass("org/apache/tuscany/UUIDUtil"); + + // Register our native invocation handler function + JNINativeMethod invokenm; + invokenm.name = const_cast<char*>("invoke"); + invokenm.signature = const_cast<char*>("(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); + invokenm.fnPtr = (void*)nativeInvoke; + env->RegisterNatives(invokerClass, &invokenm, 1); + + // Register our native UUID function + JNINativeMethod uuidnm; + uuidnm.name = const_cast<char*>("uuid"); + uuidnm.signature = const_cast<char*>("()Ljava/lang/String;"); + uuidnm.fnPtr = (void*)nativeUUID; + env->RegisterNatives(uuidClass, &uuidnm, 1); + } + + JavaVM* jvm; + JNIEnv* env; + + jclass classClass; + jclass methodClass; + jclass objectClass; + jclass doubleClass; + jclass booleanClass; + jclass stringClass; + jclass objectArrayClass; + jclass iterableClass; + jmethodID doubleValueOf; + jmethodID doubleValue; + jmethodID booleanValueOf; + jmethodID booleanValue; + jmethodID declaredMethods; + jmethodID methodName; + jmethodID parameterTypes; + jmethodID classForName; + jclass loaderClass; + jmethodID loaderValueOf; + jmethodID loaderForName; + jclass invokerClass; + jmethodID invokerValueOf; + jmethodID invokerStackTrace; + jfieldID invokerLambda; + jclass iterableUtilClass; + jmethodID iterableValueOf; + jmethodID iterableCar; + jmethodID iterableCdr; + jmethodID iterableIsNil; + jclass uuidClass; +}; + +/** + * Return the last exception that occurred in a JVM. + */ +string lastException(const JavaRuntime& jr) { + if (!jr.env->ExceptionCheck()) + return "No Exception"; + const jthrowable ex = jr.env->ExceptionOccurred(); + const jstring trace = (jstring)jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerStackTrace, ex); + const char* c = jr.env->GetStringUTFChars(trace, NULL); + const string msg(c); + jr.env->ReleaseStringUTFChars(trace, c); + jr.env->ExceptionClear(); + return msg; +} + +/** + * Declare conversion functions. + */ +const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v); +const value jobjectToValue(const JavaRuntime& jr, const jobject o); +const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v); +const list<value> jarrayToValues(const JavaRuntime& jr, const jobjectArray o); +const list<value> jiterableToValues(const JavaRuntime& jr, const jobject o); + +/** + * Convert a Java class name to a JNI class name. + */ +const bool jniClassNameHelper(char* to, const char* from) { + if (*from == '\0') { + *to = '\0'; + return true; + } + *to = *from == '.'? '/' : *from; + return jniClassNameHelper(to + 1, from + 1); +} + +const string jniClassName(const string& from) { + char buf[length(from) + 1]; + jniClassNameHelper(buf, c_str(from)); + return string(buf); +} + +/** + * Create a new Java object representing a lambda expression. + */ +class javaLambda { +public: + javaLambda(const JavaRuntime& jr, const value& iface, const lambda<value(const list<value>&)>& func) : jr(jr), iface(iface), func(func) { + } + + const value operator()(const list<value>& expr) const { + if (isNil(expr)) + return func(expr); + const value& op(car(expr)); + if (op == "equals") + return value(cadr(expr) == this); + if (op == "hashCode") + return value((double)(long)this); + if (op == "toString") { + ostringstream os; + os << this; + return value(string("org.apache.tuscany.InvocationHandler@") + (c_str(str(os)) + 2)); + } + return func(expr); + } + + const JavaRuntime& jr; + const value iface; + const lambda<value(const list<value>&)> func; +}; + +/** + * Native implementation of the InvocationHandler.invoke Java method. + * Dispatches the call to the lambda function wrapped in the invocation handler. + */ +jobject JNICALL nativeInvoke(JNIEnv* env, jobject self, unused jobject proxy, jobject method, jobjectArray args) { + + // Retrieve the lambda function from the invocation handler + jclass clazz = env->GetObjectClass(self); + jfieldID f = env->GetFieldID(clazz, "lambda", "J"); + const javaLambda& jl = *(javaLambda*)(long)env->GetLongField(self, f); + + // Retrieve the function name + const jstring s = (jstring)env->CallObjectMethod(method, jl.jr.methodName); + const char* c = env->GetStringUTFChars(s, NULL); + const value func(c); + env->ReleaseStringUTFChars(s, c); + + // Build the expression to evaluate, either (func, args[0], args[1], args[2]...) + // or just args[0] for the special eval(...) function + const list<value> expr = func == "eval"? (list<value>)car<value>(jarrayToValues(jl.jr, args)) : cons<value>(func, jarrayToValues(jl.jr, args)); + debug(expr, "java::nativeInvoke::expr"); + + // Invoke the lambda function + value result = jl(expr); + debug(result, "java::nativeInvoke::result"); + + // Convert result to a jobject + return valueToJobject(jl.jr, value(), result); +} + +/** + * Native implementation of IterableUtil.uuid. We are providing a native implementation + * of this function as java.util.UUID seems to behave differently with different JDKs. + */ +jobject JNICALL nativeUUID(JNIEnv* env) { + apr_uuid_t uuid; + apr_uuid_get(&uuid); + char buf[APR_UUID_FORMATTED_LENGTH]; + apr_uuid_format(buf, &uuid); + string s(buf, APR_UUID_FORMATTED_LENGTH); + return env->NewStringUTF(c_str(s)); +} + +/** + * Convert a lambda function to Java proxy. + */ +const jobject mkJavaLambda(const JavaRuntime& jr, unused const value& iface, const lambda<value(const list<value>&)>& l) { + const gc_ptr<javaLambda> jl = new (gc_new<javaLambda>()) javaLambda(jr, iface, l); + jclass jc = (jclass)(long)(double)iface; + const jobject obj = jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerValueOf, jc, (long)(javaLambda*)jl); + return obj; +} + +/** + * Convert a list of values to a Java jobjectArray. + */ +const jobjectArray valuesToJarrayHelper(const JavaRuntime& jr, jobjectArray a, const list<value>& v, const int i) { + if (isNil(v)) + return a; + jr.env->SetObjectArrayElement(a, i, valueToJobject(jr, value(), car(v))); + return valuesToJarrayHelper(jr, a, cdr(v), i + 1); +} + +const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v) { + jobjectArray a = jr.env->NewObjectArray(length(v), jr.objectClass, NULL); + return valuesToJarrayHelper(jr, a, v, 0); +} + +/** + * Convert a Java jobjectArray to a Java iterable. + */ +const jobject jarrayToJiterable(const JavaRuntime& jr, jobjectArray a) { + return jr.env->CallStaticObjectMethod(jr.iterableClass, jr.iterableValueOf, a); +} + +/** + * Convert a value to a Java jobject. + */ +const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v) { + switch (type(v)) { + case value::List: + return jarrayToJiterable(jr, valuesToJarray(jr, v)); + case value::Lambda: + return mkJavaLambda(jr, jtype, v); + case value::Symbol: + return jr.env->NewStringUTF(c_str(string("'") + v)); + case value::String: + return jr.env->NewStringUTF(c_str(v)); + case value::Number: + return jr.env->CallStaticObjectMethod(jr.doubleClass, jr.doubleValueOf, (double)v); + case value::Bool: + return jr.env->CallStaticObjectMethod(jr.booleanClass, jr.booleanValueOf, (bool)v); + default: + return NULL; + } +} + +/** + * Convert a list of values to an array of jvalues. + */ +const jvalue* valuesToJvaluesHelper(const JavaRuntime& jr, jvalue* a, const list<value>& types, const list<value>& v) { + if (isNil(v)) + return a; + a->l = valueToJobject(jr, car(types), car(v)); + return valuesToJvaluesHelper(jr, a + 1, cdr(types), cdr(v)); +} + +const jvalue* valuesToJvalues(const JavaRuntime& jr, const list<value>& types, const list<value>& v) { + const int n = length(v); + jvalue* a = new (gc_anew<jvalue>(n)) jvalue[n]; + valuesToJvaluesHelper(jr, a, types, v); + return a; +} + +/** + * Convert a Java jobjectArray to a list of values. + */ +const list<value> jarrayToValuesHelper(const JavaRuntime& jr, jobjectArray a, const int i, const int size) { + if (i == size) + return list<value>(); + return cons(jobjectToValue(jr, jr.env->GetObjectArrayElement(a, i)), jarrayToValuesHelper(jr, a, i + 1, size)); +} + +const list<value> jarrayToValues(const JavaRuntime& jr, jobjectArray o) { + if (o == NULL) + return list<value>(); + return jarrayToValuesHelper(jr, o, 0, jr.env->GetArrayLength(o)); +} + +/** + * Convert a Java Iterable to a list of values. + */ +const list<value> jiterableToValuesHelper(const JavaRuntime& jr, jobject o) { + if ((bool)jr.env->CallStaticBooleanMethod(jr.iterableUtilClass, jr.iterableIsNil, o)) + return list<value>(); + jobject car = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCar, o); + jobject cdr = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCdr, o); + return cons(jobjectToValue(jr, car), jiterableToValuesHelper(jr, cdr)); +} + +const list<value> jiterableToValues(const JavaRuntime& jr, jobject o) { + if (o == NULL) + return list<value>(); + return jiterableToValuesHelper(jr, o); +} + +/** + * Lambda function used to represent a Java callable object. + */ +struct javaCallable { + const JavaRuntime& jr; + const jobject obj; + + javaCallable(const JavaRuntime& jr, const jobject obj) : jr(jr), obj(obj) { + } + + const value operator()(const list<value>& args) const { + jobjectArray jargs = valuesToJarray(jr, args); + jobject result = jargs; //CallObject(func, jargs); + return jobjectToValue(jr, result); + } +}; + +/** + * Convert a Java jobject to a value. + */ +const value jobjectToValue(const JavaRuntime& jr, const jobject o) { + if (o == NULL) + return value(); + const jclass clazz = jr.env->GetObjectClass(o); + if ((jr.env->IsSameObject(clazz, jr.stringClass))) { + const char* s = jr.env->GetStringUTFChars((jstring)o, NULL); + if (*s == '\'') { + const value v(s + 1); + jr.env->ReleaseStringUTFChars((jstring)o, s); + return v; + } + const value v = string(s); + jr.env->ReleaseStringUTFChars((jstring)o, s); + return v; + } + if (jr.env->IsSameObject(clazz, jr.booleanClass)) + return value((bool)jr.env->CallBooleanMethod(o, jr.booleanValue)); + if (jr.env->IsSameObject(clazz, jr.doubleClass)) + return value((double)jr.env->CallDoubleMethod(o, jr.doubleValue)); + if (jr.env->IsAssignableFrom(clazz, jr.iterableClass)) + return jiterableToValues(jr, o); + if (jr.env->IsAssignableFrom(clazz, jr.objectArrayClass)) + return jarrayToValues(jr, (jobjectArray)o); + return lambda<value(const list<value>&)>(javaCallable(jr, o)); +} + +/** + * Returns a balanced tree of the methods of a class. + */ +const value parameterTypeToValue(const jobject t) { + return value((double)(long)t); +} + +const list<value> parameterTypesToValues(const JavaRuntime& jr, const jobjectArray t, const int i) { + if (i == 0) + return list<value>(); + return cons<value>(parameterTypeToValue(jr.env->GetObjectArrayElement(t, i - 1)), parameterTypesToValues(jr, t, i - 1)); +} + +const value methodToValue(const JavaRuntime& jr, const jobject m) { + const jobject s = jr.env->CallObjectMethod(m, jr.methodName); + const char* c = jr.env->GetStringUTFChars((jstring)s, NULL); + const string& name = string(c); + jr.env->ReleaseStringUTFChars((jstring)s, c); + + const jmethodID mid = jr.env->FromReflectedMethod(m); + + const jobjectArray t = (jobjectArray)jr.env->CallObjectMethod(m, jr.parameterTypes); + const list<value> types = reverse(parameterTypesToValues(jr, t, jr.env->GetArrayLength(t))); + + return cons<value>(c_str(name), cons<value>((double)(long)mid, types)); +} + +const list<value> methodsToValues(const JavaRuntime& jr, const jobjectArray m, const int i) { + if (i == 0) + return list<value>(); + return cons<value>(methodToValue(jr, jr.env->GetObjectArrayElement(m, i - 1)), methodsToValues(jr, m, i - 1)); +} + +const list<value> methodsToValues(const JavaRuntime& jr, const jclass clazz) { + const jobjectArray m = (jobjectArray)jr.env->CallObjectMethod(clazz, jr.declaredMethods); + return methodsToValues(jr, m, jr.env->GetArrayLength(m)); +} + +/** + * Represents a Java Class. + */ +class JavaClass { +public: + JavaClass() : loader(NULL), clazz(NULL), obj(NULL) { + } + JavaClass(const jobject loader, const jclass clazz, const jobject obj, const list<value> m) : loader(loader), clazz(clazz), obj(obj), m(m) { + } + + const jobject loader; + const jclass clazz; + const jobject obj; + const list<value> m; +}; + +/** + * Read a class. + */ +const failable<JavaClass> readClass(const JavaRuntime& jr, const string& path, const string& name) { + + // Create a class loader from the given path + const jobject jpath = jr.env->NewStringUTF(c_str(path)); + jobject loader = jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderValueOf, jpath); + + // Load the class + const jobject jname = jr.env->NewStringUTF(c_str(name)); + const jclass clazz = (jclass)jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderForName, jname, JNI_TRUE, loader); + if (clazz == NULL) + return mkfailure<JavaClass>(string("Couldn't load class: ") + name + " : " + lastException(jr)); + + // Create an instance + const jmethodID constr = jr.env->GetMethodID(clazz, "<init>", "()V"); + if (constr == NULL) + return mkfailure<JavaClass>(string("Couldn't find constructor: ") + name + " : " + lastException(jr)); + const jobject obj = jr.env->NewObject(clazz, constr); + if (obj == NULL) + return mkfailure<JavaClass>(string("Couldn't construct object: ") + name + " : " + lastException(jr)); + + return JavaClass(loader, clazz, obj, methodsToValues(jr, clazz)); +} + +/** + * Evaluate an expression against a Java class. + */ +const failable<value> evalClass(const JavaRuntime& jr, const value& expr, const JavaClass jc) { + debug(expr, "java::evalClass::expr"); + + // Lookup the Java function named as the expression operand + const list<value> func = assoc<value>(car<value>(expr), jc.m); + if (isNil(func)) { + + // The start, stop, and restart functions are optional + const value fn = car<value>(expr); + if (fn == "start" || fn == "stop") + return value(lambda<value(const list<value>&)>()); + + return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastException(jr)); + } + const jmethodID fid = (jmethodID)(long)(double)cadr(func); + + // Convert args to Java jvalues + const jvalue* args = valuesToJvalues(jr, cddr(func), cdr<value>(expr)); + + // Call the Java function + const jobject result = jr.env->CallObjectMethodA(jc.obj, fid, const_cast<jvalue*>(args)); + if (result == NULL) + return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastException(jr)); + + // Convert Java result to a value + const value v = jobjectToValue(jr, result); + debug(v, "java::evalClass::result"); + return v; +} + +} +} +#endif /* tuscany_java_eval_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/java/java-conf b/sca-cpp/branches/gcc-4.4/modules/java/java-conf new file mode 100755 index 0000000000..4c03035ca4 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/java-conf @@ -0,0 +1,29 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a Java server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +cat >>$root/conf/httpd.conf <<EOF +# Support for Java SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_java.so + +EOF + diff --git a/sca-cpp/branches/gcc-4.4/modules/java/java-shell.cpp b/sca-cpp/branches/gcc-4.4/modules/java/java-shell.cpp new file mode 100644 index 0000000000..51df513990 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/java-shell.cpp @@ -0,0 +1,40 @@ +/* + * 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$ */ + +/** + * Java evaluator shell, used for interactive testing of Java classes. + */ + +#include <assert.h> +#include "gc.hpp" +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +int main(const int argc, char** argv) { + tuscany::gc_scoped_pool pool; + if (argc != 2) { + tuscany::cerr << "Usage: java-shell <class name>" << tuscany::endl; + return 1; + } + tuscany::java::evalDriverRun(argv[1], tuscany::cin, tuscany::cout); + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/java-test.cpp b/sca-cpp/branches/gcc-4.4/modules/java/java-test.cpp new file mode 100644 index 0000000000..f811a4f58d --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/java-test.cpp @@ -0,0 +1,138 @@ +/* + * 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 Java evaluator. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +namespace tuscany { +namespace java { + +bool testEvalExpr() { + gc_scoped_pool pool; + JavaRuntime javaRuntime; + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl"); + assert(hasContent(obj)); + const value exp = mklist<value>("mult", 2, 3); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(6)); + } + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl"); + assert(hasContent(obj)); + const value exp = mklist<value>("even", 2); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(true)); + } + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.AdderImpl"); + assert(hasContent(obj)); + const value exp = mklist<value>("add", 2, 3); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(5)); + } + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl"); + assert(hasContent(obj)); + const value exp = mklist<value>("square", mklist<value>(1, 2, 3)); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == mklist<value>(1, 4, 9)); + } + return true; +} + +const value add(const list<value>& args) { + assert(car(args) == "add"); + const double x = cadr(args); + const double y = caddr(args); + return x + y; +} + +bool testEvalLambda() { + gc_scoped_pool pool; + JavaRuntime javaRuntime; + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl"); + assert(hasContent(obj)); + const value tcel = mklist<value>("add", 3, 4, lambda<value(const list<value>&)>(add)); + const failable<value> r = evalClass(javaRuntime, tcel, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(7)); + } + { + const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl"); + assert(hasContent(obj)); + const value tcel = mklist<value>("addEval", 3, 4, lambda<value(const list<value>&)>(add)); + const failable<value> r = evalClass(javaRuntime, tcel, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(7)); + } + return true; +} + +bool testClassLoader() { + gc_scoped_pool pool; + JavaRuntime javaRuntime; + const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.ClassLoader$Test"); + assert(hasContent(obj)); + const value exp = mklist<value>("testClassLoader"); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(true)); + return true; +} + +bool testIterableUtil() { + gc_scoped_pool pool; + JavaRuntime javaRuntime; + const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.IterableUtil$Test"); + assert(hasContent(obj)); + const value exp = mklist<value>("testList"); + const failable<value> r = evalClass(javaRuntime, exp, content(obj)); + assert(hasContent(r)); + assert(content(r) == value(true)); + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::java::testEvalExpr(); + tuscany::java::testEvalLambda(); + tuscany::java::testClassLoader(); + tuscany::java::testIterableUtil(); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/jni-test.cpp b/sca-cpp/branches/gcc-4.4/modules/java/jni-test.cpp new file mode 100644 index 0000000000..727af13dc6 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/jni-test.cpp @@ -0,0 +1,80 @@ +/* + * 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$ */ + +/** + * Basic JNI test. + */ + +#include <assert.h> +#include <jni.h> +#include "stream.hpp" +#include "string.hpp" +#include "fstream.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace java { + +#ifdef JAVA_HARMONY_VM +#define JNI_VERSION JNI_VERSION_1_4 +#else +#define JNI_VERSION JNI_VERSION_1_6 +#endif + +bool testJNI() { + gc_scoped_pool pool; + JavaVM* jvm; + JNIEnv* env; + + JavaVMInitArgs args; + args.version = JNI_VERSION; + args.ignoreUnrecognized = JNI_FALSE; + JavaVMOption options[3]; + args.options = options; + args.nOptions = 0; + const char* envcp = getenv("CLASSPATH"); + const string cp = string("-Djava.class.path=") + (envcp == NULL? "." : envcp); + options[args.nOptions].optionString = const_cast<char*>(c_str(cp)); + options[args.nOptions++].extraInfo = NULL; +#ifdef JAVA_HARMONY_VM + JNI_CreateJavaVM(&jvm, &env, &args); +#else + JNI_CreateJavaVM(&jvm, (void**)&env, &args); +#endif + + jclass classClass = env->FindClass("java/lang/Class"); + assert(classClass != NULL); + jclass loaderClass = env->FindClass("org/apache/tuscany/ClassLoader"); + assert(loaderClass != NULL); + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::java::testJNI(); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/mod-java.cpp b/sca-cpp/branches/gcc-4.4/modules/java/mod-java.cpp new file mode 100644 index 0000000000..510f9574b0 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/mod-java.cpp @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval Java component implementations. + */ + +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../server/mod-cpp.hpp" +#include "../server/mod-eval.hpp" +#include "mod-java.hpp" + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Apply a lifecycle start or restart event. + */ +struct javaLifecycle { + javaLifecycle(java::JavaRuntime& jr) : jr(jr) { + } + const value operator()(const list<value>& params) const { + const value func = car(params); + if (func == "javaRuntime") + return (gc_ptr<value>)(value*)(void*)&jr; + return lambda<value(const list<value>&)>(); + } + java::JavaRuntime& jr; +}; + +const value applyLifecycle(unused const list<value>& params) { + + // Create a Java runtime + java::JavaRuntime& jr = *(new (gc_new<java::JavaRuntime>()) java::JavaRuntime()); + + // Return the function to invoke on subsequent events + return failable<value>(lambda<value(const list<value>&)>(javaLifecycle(jr))); +} + +/** + * Evaluate a Java component implementation and convert it to an applicable + * lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle) { + const string itype(elementName(impl)); + if (contains(itype, ".java")) { + const void* p = (gc_ptr<value>)lifecycle(mklist<value>("javaRuntime")); + return modjava::evalImplementation(path, impl, px, *(java::JavaRuntime*)p); + } + if (contains(itype, ".cpp")) + return modcpp::evalImplementation(path, impl, px); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/mod-java.hpp b/sca-cpp/branches/gcc-4.4/modules/java/mod-java.hpp new file mode 100644 index 0000000000..e7da06e930 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/mod-java.hpp @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modjava_hpp +#define tuscany_modjava_hpp + +/** + * Evaluation functions used by mod-eval to evaluate Java + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace server { +namespace modjava { + +/** + * Apply a Java component implementation function. + */ +struct applyImplementation { + java::JavaClass impl; + const list<value> px; + java::JavaRuntime& jr; + applyImplementation(const java::JavaClass& impl, const list<value>& px, java::JavaRuntime& jr) : impl(impl), px(px), jr(jr) { + } + const value operator()(const list<value>& params) const { + const value expr = append<value>(params, px); + debug(expr, "modeval::java::applyImplementation::input"); + const failable<value> res = java::evalClass(jr, expr, impl); + const value val = !hasContent(res)? mklist<value>(value(), reason(res)) : mklist<value>(content(res)); + debug(val, "modeval::java::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a Java component implementation and convert it to an applicable + * lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, java::JavaRuntime& jr) { + const string cn(attributeValue("class", impl)); + const failable<java::JavaClass> jc = java::readClass(jr, path, cn); + if (!hasContent(jc)) + return mkfailure<lambda<value(const list<value>&)> >(reason(jc)); + return lambda<value(const list<value>&)>(applyImplementation(content(jc), px, jr)); +} + +} +} +} + +#endif /* tuscany_modjava_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/ClassLoader.java b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/ClassLoader.java new file mode 100644 index 0000000000..ef7b2316fb --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/ClassLoader.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package org.apache.tuscany; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Class loader used to load SCA component implementation classes. + */ +class ClassLoader extends URLClassLoader { + + ClassLoader(final URL... urls) { + super(urls); + } + + /** + * Create a class loader for an SCA contribution path. + */ + static java.lang.ClassLoader valueOf(final String path) throws MalformedURLException { + return new ClassLoader(new File(path).toURI().toURL()); + } + + /** + * Load a class. + */ + static Class<?> forName(final String name, final boolean resolve, final java.lang.ClassLoader loader) throws ClassNotFoundException { + return Class.forName(name, resolve, loader); + } + + /** + * Test the class loader. + */ + static class Test { + Boolean testClassLoader() { + try { + final Class<?> clazz = ClassLoader.forName("test.CalcImpl", true, ClassLoader.valueOf(".")); + assert clazz != null; + } catch(final MalformedURLException e) { + throw new RuntimeException(e); + } catch(final ClassNotFoundException e) { + throw new RuntimeException(e); + } + return true; + } + } + + public static void main(final String[] args) { + System.out.println("Testing..."); + + Test.class.getClassLoader().setDefaultAssertionStatus(true); + new Test().testClassLoader(); + + System.out.println("OK"); + } + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/InvocationHandler.java b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/InvocationHandler.java new file mode 100644 index 0000000000..06466fe9fc --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/InvocationHandler.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package org.apache.tuscany; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Proxy Invocation handler used to represent SCA component references. + */ +class InvocationHandler implements java.lang.reflect.InvocationHandler { + final long lambda; + + InvocationHandler(final long lambda) { + this.lambda = lambda; + } + + /** + * Create a proxy for an interface and the lambda function representing + * an SCA component reference. + */ + static Object valueOf(final Class<?> iface, final long lambda) { + return Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, new InvocationHandler(lambda)); + } + + /** + * Proxy invocation of a C++ function. + */ + public native Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable; + + /** + * Return the stack trace of an exception. + */ + static String stackTrace(final Throwable e) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/IterableUtil.java b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/IterableUtil.java new file mode 100644 index 0000000000..6d559f370a --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/IterableUtil.java @@ -0,0 +1,368 @@ +/* + * 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. + */ + +package org.apache.tuscany; + +import static java.util.Arrays.*; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Utility functions to help work efficiently with iterable lists, inspired from Lisp. + */ +public class IterableUtil { + + /** + * Convert an array or a variable list of arguments to an iterable list. + */ + public static <T> Iterable<T> list(final Object... a) { + return new ArrayIterable<T>(a, 0); + } + + /** + * Convert an iterable list to a java.util.Collection. + */ + @SuppressWarnings("unchecked") + public static <T> Collection<T> collection(final Object l) { + final Collection<T> c = new ArrayList<T>(); + for(final Object x : (Iterable<?>)l) + c.add((T)x); + return c; + } + + /** + * Construct a new list from an element and a list. + */ + public static <T> Iterable<T> cons(final Object car, final Iterable<?> cdr) { + return new PairIterable<T>(car, cdr); + } + + /** + * Return true if a list is nil (empty). + */ + public static boolean isNil(final Object l) { + if(l instanceof BasicIterable<?>) + return ((BasicIterable<?>)l).isNil(); + if(l instanceof Collection<?>) + return ((Collection<?>)l).isEmpty(); + return !((Iterable<?>)l).iterator().hasNext(); + } + + /** + * Return the car (first element) of a list. + */ + @SuppressWarnings("unchecked") + public static <T> T car(final Object l) { + if(l instanceof BasicIterable<?>) + return ((BasicIterable<T>)l).car(); + if(l instanceof List<?>) + return (T)((List<?>)l).get(0); + return (T)((Iterable<?>)l).iterator().next(); + } + + /** + * Return the cdr (rest after the first element) of a list. + */ + @SuppressWarnings("unchecked") + public static <T> Iterable<T> cdr(final Object l) { + if(l instanceof BasicIterable<?>) + return ((BasicIterable<T>)l).cdr(); + if(l instanceof List<?>) + return new ListIterable<T>((List<?>)l, 1); + if(l instanceof Collection<?>) + return new ArrayIterable<T>(((Collection<?>)l).toArray(), 1); + return new Iterable<T>() { + public Iterator<T> iterator() { + final Iterator<T> i = ((Iterable<T>)l).iterator(); + i.next(); + return i; + } + }; + } + + /** + * Return the car of the cdr of a list. + */ + @SuppressWarnings("unchecked") + public static <T> T cadr(final Object l) { + return (T)car(cdr(l)); + } + + /** + * Return the cdr of the cdr of a list. + */ + public static <T> Iterable<T> cddr(final Object l) { + return cdr(cdr(l)); + } + + /** + * Return the car of the cdr of the cdr of a list. + */ + @SuppressWarnings("unchecked") + public static <T> T caddr(final Object l) { + return (T)car(cddr(l)); + } + + /** + * Return the first pair matching a key from a list of key value pairs. + */ + public static <T> Iterable<T> assoc(final Object k, final Object l) { + if(isNil(l)) + return list(); + if(k.equals(car(car(l)))) + return car(l); + return assoc(k, cdr(l)); + } + + /** + * Internal base implementation class for iterable and immutable lists. + */ + static abstract class BasicIterable<T> extends AbstractList<T> { + abstract T car(); + + abstract Iterable<T> cdr(); + + abstract Boolean isNil(); + + @Override + public int size() { + return this.isNil()? 0 : 1 + ((List<T>)this.cdr()).size(); + } + + @Override + public T get(final int index) { + throw new UnsupportedOperationException(); + } + } + + /** + * Internal implementation of a list backed by an array. + */ + static class ArrayIterable<T> extends BasicIterable<T> { + final Object[] a; + final int start; + + ArrayIterable(final Object[] a, final int start) { + this.a = a; + this.start = start; + } + + @Override + Boolean isNil() { + return this.a.length - this.start == 0; + } + + @SuppressWarnings("unchecked") + @Override + T car() { + return (T)this.a[this.start]; + } + + @Override + BasicIterable<T> cdr() { + return new ArrayIterable<T>(this.a, this.start + 1); + } + + @Override + public Iterator<T> iterator() { + return new Iterator<T>() { + int i = ArrayIterable.this.start; + + public boolean hasNext() { + return this.i < ArrayIterable.this.a.length; + } + + @SuppressWarnings("unchecked") + public T next() { + return (T)ArrayIterable.this.a[this.i++]; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * Internal implementation of a list backed by a java.util.List. + */ + static class ListIterable<T> extends BasicIterable<T> { + final List<?> l; + final int start; + + ListIterable(final List<?> l, final int start) { + this.l = l; + this.start = start; + } + + @Override + Boolean isNil() { + return this.l.size() - this.start == 0; + } + + @SuppressWarnings("unchecked") + @Override + T car() { + return (T)this.l.get(this.start); + } + + @Override + BasicIterable<T> cdr() { + return new ListIterable<T>(this.l, this.start + 1); + } + + @Override + public Iterator<T> iterator() { + return new Iterator<T>() { + int i = ListIterable.this.start; + + public boolean hasNext() { + return this.i < ListIterable.this.l.size(); + } + + @SuppressWarnings("unchecked") + public T next() { + return (T)ListIterable.this.l.get(this.i++); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * Internal implementation of a list backed by an element / iterable pair. + */ + static class PairIterable<T> extends BasicIterable<T> { + final Object car; + final Iterable<?> cdr; + + PairIterable(final Object car, final Iterable<?> cdr) { + this.car = car; + this.cdr = cdr; + } + + @Override + Boolean isNil() { + return false; + } + + @SuppressWarnings("unchecked") + @Override + T car() { + return (T)this.car; + } + + @SuppressWarnings("unchecked") + @Override + Iterable<T> cdr() { + return (Iterable<T>)this.cdr; + } + + @Override + public Iterator<T> iterator() { + return new Iterator<T>() { + boolean carIterator = true; + Iterator<?> cdrIterator = PairIterable.this.cdr.iterator(); + + public boolean hasNext() { + if(this.carIterator) + return true; + return this.cdrIterator.hasNext(); + } + + @SuppressWarnings("unchecked") + public T next() { + if(this.carIterator) { + this.carIterator = false; + return (T)PairIterable.this.car; + } + return (T)this.cdrIterator.next(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * Test the list functions. + */ + static class Test { + Boolean testList() { + final Iterable<Object> l = list(2, 3, 4); + assert car(l) == Integer.valueOf(2); + assert cadr(l) == Integer.valueOf(3); + assert caddr(l) == Integer.valueOf(4); + + final Iterable<Object> c = cons(0, cons(1, l)); + assert car(c) == Integer.valueOf(0); + assert cadr(c) == Integer.valueOf(1); + assert caddr(c) == Integer.valueOf(2); + assert c.toString().equals("[0, 1, 2, 3, 4]"); + + final Iterable<Object> cl = cons(0, cons(1, new ArrayList<Object>(asList(2, 3, 4)))); + assert car(cl) == Integer.valueOf(0); + assert cadr(cl) == Integer.valueOf(1); + assert caddr(cl) == Integer.valueOf(2); + assert cl.toString().equals("[0, 1, 2, 3, 4]"); + + final List<Object> jl = new ArrayList<Object>(collection(cl)); + assert jl.size() == 5; + assert jl.get(0) == Integer.valueOf(0); + assert jl.get(1) == Integer.valueOf(1); + assert jl.get(2) == Integer.valueOf(2); + + final Iterable<Object> n = list(); + assert isNil(n); + assert n.toString().equals("[]"); + + final Iterable<Object> cn = cons(0, n); + assert !isNil(cn); + assert isNil(cdr(cn)); + assert cn.toString().equals("[0]"); + + final Iterable<Object> al = new ArrayList<Object>(Arrays.asList(1, 2, 3)); + assert car(al) == Integer.valueOf(1); + assert cadr(al) == Integer.valueOf(2); + assert caddr(al) == Integer.valueOf(3); + return true; + } + } + + public static void main(final String[] args) { + System.out.println("Testing..."); + + Test.class.getClassLoader().setDefaultAssertionStatus(true); + new Test().testList(); + + System.out.println("OK"); + } + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/Service.java b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/Service.java new file mode 100644 index 0000000000..a00d5b1b53 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/Service.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package org.apache.tuscany; + +/** + * Interface used to represent SCA component references providing both REST + * access to a resource and function application. + */ +public interface Service { + + /** + * Post a new item to a collection of items. + */ + Iterable<String> post(Iterable<String> collection, Iterable<?> item); + + /** + * Return an item. + */ + Iterable<?> get(Iterable<String> id); + + /** + * Update an item. + */ + boolean put(Iterable<String> id, Iterable<?> item); + + /** + * Delete an item. + */ + boolean delete(Iterable<String> id); + + /** + * Evaluate an expression. + */ + <T> T eval(Object... params); + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/UUIDUtil.java b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/UUIDUtil.java new file mode 100644 index 0000000000..60076c62ca --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/org/apache/tuscany/UUIDUtil.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.apache.tuscany; + +/** + * A fast and portable UUID generator function. + */ +public class UUIDUtil { + + /** + * Return a UUID. + */ + public static native String uuid(); + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/server-test b/sca-cpp/branches/gcc-4.4/modules/java/server-test new file mode 100755 index 0000000000..dba63a9525 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/server-test @@ -0,0 +1,41 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +../http/httpd-conf tmp localhost 8090 ../server/htdocs +../server/server-conf tmp +./java-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`" + +../http/httpd-start tmp +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +../http/httpd-stop tmp +sleep 2 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/Adder.java b/sca-cpp/branches/gcc-4.4/modules/java/test/Adder.java new file mode 100644 index 0000000000..7236548c41 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/Adder.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package test; + +public interface Adder { + + Double add(Double x, Double y); + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/AdderImpl.java b/sca-cpp/branches/gcc-4.4/modules/java/test/AdderImpl.java new file mode 100644 index 0000000000..e607012b78 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/AdderImpl.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package test; + +public class AdderImpl { + + public Double add(Double x, Double y) { + return x + y; + } + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/CalcImpl.java b/sca-cpp/branches/gcc-4.4/modules/java/test/CalcImpl.java new file mode 100644 index 0000000000..5bea01a43f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/CalcImpl.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +package test; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.tuscany.Service; + +public class CalcImpl { + + public Double add(final Double x, final Double y, final Adder adder) { + return adder.add(x, y); + } + + public Double addEval(final Double x, final Double y, final Service adder) { + return adder.eval("add", x, y); + } + + public Double mult(final Double x, final Double y) { + return x * y; + } + + public Boolean even(final Double x) { + return (double)((int)(double)x / 2 * 2) == (double)x; + } + + public Iterable<Double> square(final Iterable<Double> l) { + final List<Double> r = new ArrayList<Double>(); + for(final Double x : l) + r.add(x * x); + return r; + } + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/Client.java b/sca-cpp/branches/gcc-4.4/modules/java/test/Client.java new file mode 100644 index 0000000000..c3bd875fcc --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/Client.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package test; + +public interface Client { + + String echo(String x); + + Iterable<?> get(Iterable<String> id); + + Iterable<String> post(Iterable<String> collection, Iterable<?> item); + + Boolean put(Iterable<String> id, Iterable<?> item); + + Boolean delete(Iterable<String> id); + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/ClientImpl.java b/sca-cpp/branches/gcc-4.4/modules/java/test/ClientImpl.java new file mode 100644 index 0000000000..ade2ba302e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/ClientImpl.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package test; + +public class ClientImpl { + + public String echo(String x, Server server) { + return server.echo(x); + } + + public Iterable<?> get(Iterable<String> id, Server server) { + return server.get(id); + } + + public Iterable<String> post(Iterable<String> collection, Iterable<?> item, Server server) { + return server.post(collection, item); + } + + public Boolean put(Iterable<String> id, Iterable<?> item, Server server) { + return server.put(id, item); + } + + public Boolean delete(Iterable<String> id, Server server) { + return server.delete(id); + } + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/Server.java b/sca-cpp/branches/gcc-4.4/modules/java/test/Server.java new file mode 100644 index 0000000000..3dfe3c84ef --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/Server.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package test; + +public interface Server { + + String echo(String x); + + Iterable<?> get(Iterable<String> id); + + Iterable<String> post(Iterable<String> collection, Iterable<?> item); + + Boolean put(Iterable<String> id, Iterable<?> item); + + Boolean delete(Iterable<String> id); + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/test/ServerImpl.java b/sca-cpp/branches/gcc-4.4/modules/java/test/ServerImpl.java new file mode 100644 index 0000000000..05012c22cf --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/test/ServerImpl.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package test; + +import static org.apache.tuscany.IterableUtil.*; + +public class ServerImpl { + + public String echo(final String x) { + return x; + } + + public Iterable<?> get(final Iterable<String> id) { + if (isNil(id)) + return list("Sample Feed", "123456789", + list("Item", "111", list(list("'javaClass", "services.Item"), list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99))), + list("Item", "222", list(list("'javaClass", "services.Item"), list("'name", "Orange"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 3.55))), + list("Item", "333", list(list("'javaClass", "services.Item"), list("'name", "Pear"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 1.55)))); + final Iterable<?> entry = list(list("'javaClass", "services.Item"), list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99)); + return list("Item", car(id), entry); + } + + public Iterable<String> post(final Iterable<String> collection, final Iterable<?> item) { + return list("123456789"); + } + + public Boolean put(final Iterable<String> id, final Iterable<?> item) { + return true; + } + + public Boolean delete(final Iterable<String> id) { + return true; + } +} diff --git a/sca-cpp/branches/gcc-4.4/modules/java/wiring-test b/sca-cpp/branches/gcc-4.4/modules/java/wiring-test new file mode 100755 index 0000000000..dcbaa3e1cc --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/java/wiring-test @@ -0,0 +1,80 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 ../server/htdocs +../server/server-conf tmp +./java-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`" + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html ../server/htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml ../server/htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml ../server/htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @../server/htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt ../server/htdocs/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/json/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/json/Makefile.am new file mode 100644 index 0000000000..fd00ebb4af --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/json/Makefile.am @@ -0,0 +1,22 @@ +# 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. + +json_test_SOURCES = json-test.cpp +json_test_LDFLAGS = -lmozjs + +noinst_PROGRAMS = json-test +TESTS = json-test diff --git a/sca-cpp/branches/gcc-4.4/modules/json/json-test.cpp b/sca-cpp/branches/gcc-4.4/modules/json/json-test.cpp new file mode 100644 index 0000000000..b74f068710 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/json/json-test.cpp @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test JSON data conversion functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "json.hpp" + +namespace tuscany { +namespace json { + +bool testJSEval() { + JSONContext cx; + const string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)"); + jsval rval; + assert(JS_EvaluateScript(cx, cx.getGlobal(), c_str(script), length(script), "testJSON.js", 1, &rval)); + const string r(JS_GetStringBytes(JS_ValueToString(cx, rval))); + assert(r == "5"); + return true; +} + +ostream* jsonWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +bool testJSON() { + const JSONContext cx; + + { + const list<value> ad = mklist<value>(mklist<value>(attribute, "city", string("san francisco")), mklist<value>(attribute, "state", string("ca"))); + 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}}}"); + } + { + 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> (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\"}"); + + istringstream is(str(os)); + const list<string> il = streamList(is); + const list<value> r = content(readJSON(il, cx)); + assert(r == l); + + ostringstream wos; + 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!"))); + 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; +} + +bool testJSONRPC() { + JSONContext cx; + { + const string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}"); + const list<value> e = content(readJSON(mklist(lm), cx)); + const list<value> v = elementsToValues(e); + assert(assoc<value>("id", v) == mklist<value>("id", 1)); + assert(assoc<value>("method", v) == mklist<value>("method", string("system.listMethods"))); + assert(assoc<value>("params", v) == mklist<value>("params", list<value>())); + } + { + const string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}"); + const list<value> e = content(readJSON(mklist(i), cx)); + const string i2("{\"id\":3,\"result\":{\"0\":{\"price\":\"$2.99\",\"name\":\"Apple\"},\"1\":{\"price\":\"$3.55\",\"name\":\"Orange\"},\"2\":{\"price\":\"$1.55\",\"name\":\"Pear\"}}}"); + const list<value> e2 = content(readJSON(mklist(i), cx)); + assert(e == e2); + } + { + const string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}"); + const list<value> e = content(readJSON(mklist(i), cx)); + ostringstream os; + write(content(writeJSON(e, cx)), os); + assert(str(os) == i); + const list<value> v = elementsToValues(e); + const list<value> r = valuesToElements(v); + assert(r == e); + } + { + const list<value> r = mklist<value>(mklist<value>("id", 1), mklist<value>("result", mklist<value>(string("Service.get"), string("Service.getTotal")))); + const list<value> e = valuesToElements(r); + ostringstream os; + write(content(writeJSON(e, cx)), os); + assert(str(os) == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}"); + } + { + const string f("{\"id\":1,\"result\":[\"Sample Feed\",\"123456789\",[\"Item\",\"111\",{\"javaClass\":\"services.Item\",\"name\":\"Apple\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":2.99}],[\"Item\",\"222\",{\"javaClass\":\"services.Item\",\"name\":\"Orange\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":3.55}],[\"Item\",\"333\",{\"javaClass\":\"services.Item\",\"name\":\"Pear\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":1.55}]]}"); + const list<value> r = content(readJSON(mklist(f), cx)); + const list<value> v = elementsToValues(r); + const list<value> e = valuesToElements(v); + ostringstream os; + 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; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::json::testJSEval(); + tuscany::json::testJSON(); + tuscany::json::testJSONRPC(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/json/json.hpp b/sca-cpp/branches/gcc-4.4/modules/json/json.hpp new file mode 100644 index 0000000000..1d966d3f67 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/json/json.hpp @@ -0,0 +1,403 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_json_hpp +#define tuscany_json_hpp + +/** + * JSON data conversion functions. + */ + +#define XP_UNIX +#include <jsapi.h> +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace json { + +/** + * Report JSON errors. + */ +void reportError(unused JSContext *cx, const char *message, JSErrorReport *report) { + cerr << (const char*)(report->filename? report->filename : "<no filename>") << ":" + << (int)report->lineno << ":" << message << endl; +} + +/** + * Encapsulates a JavaScript runtime. Shared by multiple threads in + * a process. + */ +class JSONRuntime { +public: + JSONRuntime() { + // Create JS runtime + rt = JS_NewRuntime(8L * 1024L * 1024L); + if(rt == NULL) + cleanup(); + } + + operator JSRuntime*() const { + return rt; + } +private: + bool cleanup() { + if(rt != NULL) { + JS_DestroyRuntime(rt); + rt = NULL; + } + JS_ShutDown(); + return true; + } + + JSRuntime* rt; +} jsRuntime; + +JSClass jsGlobalClass = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS}; + +/** + * Represents a JavaScript context. Create one per thread. + */ +class JSONContext { +public: + JSONContext() { + // Create JS context + cx = JS_NewContext(jsRuntime, 8192); + if(cx == NULL) + return; + JS_SetOptions(cx, JSOPTION_VAROBJFIX); + JS_SetVersion(cx, JSVERSION_DEFAULT); + JS_SetErrorReporter(cx, reportError); + + // Create global JS object + global = JS_NewObject(cx, &jsGlobalClass, NULL, NULL); + if(global == NULL) { + cleanup(); + return; + } + + // Populate global object with the standard globals, like Object and Array + if(!JS_InitStandardClasses(cx, global)) { + cleanup(); + return; + } + } + + ~JSONContext() { + cleanup(); + } + + operator JSContext*() const { + return cx; + } + + JSObject* getGlobal() const { + return global; + } + +private: + bool cleanup() { + if(cx != NULL) { + JS_DestroyContext(cx); + cx = NULL; + } + return true; + } + + JSContext* cx; + JSObject* global; +}; + +/** + * 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(!isNil((list<value>)v) && 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) { + + const value jsValToValue(const jsval& jsv, const JSONContext& cx); + + jsid id; + if(!JS_NextProperty(cx, i, &id) || id == JSVAL_VOID) + return propertiesSoFar; + jsval jsv; + if(!JS_GetPropertyById(cx, o, id, &jsv)) + return propertiesSoFar; + const value val = jsValToValue(jsv, cx); + + jsval idv; + JS_IdToValue(cx, id, &idv); + if(JSVAL_IS_STRING(idv)) { + const 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); +} + +/** + * Converts a JS val to a value. + */ +const value jsValToValue(const jsval& jsv, const JSONContext& cx) { + switch(JS_TypeOfValue(cx, jsv)) { + case JSTYPE_STRING: { + return value(string(JS_GetStringBytes(JSVAL_TO_STRING(jsv)))); + } + case JSTYPE_BOOLEAN: { + return value((bool)JSVAL_TO_BOOLEAN(jsv)); + } + case JSTYPE_NUMBER: { + jsdouble jsd; + JS_ValueToNumber(cx, jsv, &jsd); + return value((double)jsd); + } + case JSTYPE_OBJECT: { + JSObject* o = JSVAL_TO_OBJECT(jsv); + JSObject* i = JS_NewPropertyIterator(cx, o); + if(i == NULL) + return value(list<value> ()); + const value pv = jsPropertiesToValues(list<value> (), o, i, cx); + return pv; + } + default: { + return value(); + } + } +} + +/** + * Consumes JSON strings and populates a JS object. + */ +failable<bool> consume(JSONParser* parser, const list<string>& ilist, const JSONContext& cx) { + if (isNil(ilist)) + return true; + JSString* jstr = JS_NewStringCopyZ(cx, c_str(car(ilist))); + if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr))) + return mkfailure<bool>("JS_ConsumeJSONText failed"); + return consume(parser, cdr(ilist), cx); +} + +/** + * Convert a list of strings representing a JSON document to a list of values. + */ +const failable<list<value> > readJSON(const list<string>& ilist, const JSONContext& cx) { + jsval val; + JSONParser* parser = JS_BeginJSONParse(cx, &val); + if(parser == NULL) + return mkfailure<list<value> >("JS_BeginJSONParse failed"); + + const failable<bool> consumed = consume(parser, ilist, cx); + + if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL)) + return mkfailure<list<value> >("JS_FinishJSONParse failed"); + if(!hasContent(consumed)) + return mkfailure<list<value> >(reason(consumed)); + + return list<value>(jsValToValue(val, cx)); +} + +/** + * Converts a list of values to JS array elements. + */ +JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSONContext& cx) { + const jsval valueToJSVal(const value& val, const JSONContext& cx); + if (isNil(l)) + return a; + jsval pv = valueToJSVal(car(l), cx); + JS_SetElement(cx, a, i, &pv); + return valuesToJSElements(a, cdr(l), ++i, cx); +} + +/** + * 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: { + return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c_str((string)val))); + } + case value::Bool: { + return BOOLEAN_TO_JSVAL((bool)val); + } + case value::Number: { + return DOUBLE_TO_JSVAL(JS_NewDouble(cx, (double)val)); + } + case value::List: { + if (isJSArray(val)) + return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx)); + return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx)); + } + default: { + return JSVAL_VOID; + } + } +} + +/** + * Converts a list of values to JS properties. + */ +JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) { + if (isNil(l)) + 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(atsign + string(attributeName(token))), &pv); + + } else if (isTaggedList(token, element)) { + + // Write the value of an element + if (elementHasValue(token)) { + jsval pv = valueToJSVal(elementValue(token), cx); + JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv); + + } else { + + // Write a parent element + JSObject* child = JS_NewObject(cx, NULL, NULL, NULL); + jsval pv = OBJECT_TO_JSVAL(child); + JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv); + + // Write its children + valuesToJSProperties(child, elementChildren(token), cx); + } + } + + // Go on + return valuesToJSProperties(o, cdr(l), cx); +} + +/** + * Context passed to the JSON write callback function. + */ +template<typename R> class WriteContext { +public: + WriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum, const JSONContext& cx) : cx(cx), reduce(reduce), accum(accum) { + } + const JSONContext& cx; + const lambda<R(const string&, const R)> reduce; + R accum; +}; + +/** + * Called by JS_Stringify to write JSON out. + */ +template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *data) { + WriteContext<R>& wcx = *(static_cast<WriteContext<R>*> (data)); + JSString* jstr = JS_NewUCStringCopyN(wcx.cx, buf, len); + wcx.accum = wcx.reduce(string(JS_GetStringBytes(jstr), JS_GetStringLength(jstr)), wcx.accum); + return JS_TRUE; +} + +/** + * Convert a list of values to a JSON document. + */ +template<typename R> const failable<R> writeJSON(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) { + 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)) + return mkfailure<R>("JS_Stringify failed"); + return wcx.accum; +} + +/** + * Convert a list of values to a list of strings representing a JSON document. + */ +const failable<list<string> > writeJSON(const list<value>& l, const JSONContext& cx) { + const failable<list<string> > ls = writeJSON<list<string>>(rcons<string>, list<string>(), l, cx); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); +} + +/** + * 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)); + return writeJSON(valuesToElements(r), cx); +} + +/** + * 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. + */ +const string funcName(const string& f) { + if (length(f) > 7 && find(f, "system.", 0) == 0) + return c_str(f) + 7; + if (length(f) > 8 && find(f, "Service.", 0) == 0) + return c_str(f) + 8; + return f; +} + +} +} + +#endif /* tuscany_json_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/python/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/python/Makefile.am new file mode 100644 index 0000000000..c4e7b038e1 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/Makefile.am @@ -0,0 +1,54 @@ +# 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. + +libdir = $(prefix)/modules/python + +if WANT_PYTHON + +INCLUDES = -I${PYTHON_INCLUDE} + +mod_SCRIPTS = python-conf +moddir = $(prefix)/modules/python + +nobase_xsd_DATA = *.xsd +xsddir = $(prefix)/xsd + +prefix_DATA = python.prefix +prefixdir = $(prefix)/modules/python +python.prefix: $(top_builddir)/config.status + echo ${PYTHON_PREFIX} >python.prefix + +lib_LTLIBRARIES = libmod_tuscany_python.la +libmod_tuscany_python_la_SOURCES = mod-python.cpp +libmod_tuscany_python_la_LDFLAGS = -lxml2 -lcurl -lmozjs -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6 +noinst_DATA = libmod_tuscany_python.so +libmod_tuscany_python.so: + ln -s .libs/libmod_tuscany_python.so libmod_tuscany_python.so + +python_test_SOURCES = python-test.cpp +python_test_LDFLAGS = -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6 + +python_shell_SOURCES = python-shell.cpp +python_shell_LDFLAGS = -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6 + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +noinst_PROGRAMS = python-test python-shell client-test +TESTS = python-test server-test + +endif diff --git a/sca-cpp/branches/gcc-4.4/modules/python/client-test.cpp b/sca-cpp/branches/gcc-4.4/modules/python/client-test.cpp new file mode 100644 index 0000000000..21fda53e05 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/client-test.cpp @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "../server/client-test.hpp" + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::server::testURI = "http://localhost:8090/python"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/python/client-test.py b/sca-cpp/branches/gcc-4.4/modules/python/client-test.py new file mode 100644 index 0000000000..47e6cf4bda --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/client-test.py @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# JSON-RPC test case + +def echo(x, ref): + return ref("echo", x) + +# ATOMPub test case + +def get(id, ref): + return ref("get", id) + +def post(collection, item, ref): + return ref("post", collection, item) + +def put(id, item, ref): + return ref("put", id, item) + +def delete(id, ref): + return ref("delete", id) diff --git a/sca-cpp/branches/gcc-4.4/modules/python/domain-test.composite b/sca-cpp/branches/gcc-4.4/modules/python/domain-test.composite new file mode 100644 index 0000000000..c8e92b286e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/domain-test.composite @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="python-test"> + <t:implementation.python script="server-test.py"/> + <service name="test"> + <t:binding.http uri="python"/> + </service> + </component> + + <component name="client-test"> + <t:implementation.python script="client-test.py"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="python-test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/gcc-4.4/modules/python/driver.hpp b/sca-cpp/branches/gcc-4.4/modules/python/driver.hpp new file mode 100644 index 0000000000..2820201057 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/driver.hpp @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_python_driver_hpp +#define tuscany_python_driver_hpp + +/** + * Python evaluator main driver loop. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "monad.hpp" +#include "../scheme/driver.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace python { + +const value evalDriverLoop(PyObject* script, istream& in, ostream& out) { + scheme::promptForInput(scheme::evalInputPrompt, out); + value input = scheme::readValue(in); + if (isNil(input)) + return input; + const failable<value> output = evalScript(input, script); + scheme::announceOutput(scheme::evalOutputPrompt, out); + scheme::userPrint(content(output), out); + return evalDriverLoop(script, in, out); +} + +const bool evalDriverRun(const char* path, istream& in, ostream& out) { + PythonRuntime py; + scheme::setupDisplay(out); + ifstream is(path); + failable<PyObject*> script = readScript(path, is); + if (!hasContent(script)) + return true; + evalDriverLoop(content(script), in, out); + Py_DECREF(content(script)); + return true; +} + +} +} +#endif /* tuscany_python_driver_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/python/eval.hpp b/sca-cpp/branches/gcc-4.4/modules/python/eval.hpp new file mode 100644 index 0000000000..855804a5ae --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/eval.hpp @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_python_eval_hpp +#define tuscany_python_eval_hpp + +/** + * Python script evaluation logic. + */ +#include <python2.6/Python.h> + +#include "list.hpp" +#include "value.hpp" + +namespace tuscany { +namespace python { + +/** + * Represent a Python runtime. + */ +class PythonRuntime { +public: + PythonRuntime() { + if (Py_IsInitialized()) + return; + Py_InitializeEx(0); + } +}; + +/** + * Return the last python error. + */ +const string lastError() { + if(PyErr_Occurred()) { + PyObject* type; + PyObject* val; + PyObject* trace; + PyErr_Fetch(&type, &val, &trace); + if (type != NULL && val != NULL) { + PyObject* stype = PyObject_Str(type); + PyObject* sval = PyObject_Str(val); + string msg = string() + PyString_AsString(stype) + " : " + PyString_AsString(sval); + Py_DECREF(stype); + Py_DECREF(sval); + Py_DECREF(type); + Py_DECREF(val); + Py_XDECREF(trace); + PyErr_Print(); + return msg; + } + PyErr_Print(); + Py_XDECREF(type); + Py_XDECREF(val); + Py_XDECREF(trace); + PyErr_Print(); + return "Unknown Python error"; + } + return ""; +} + +/** + * Declare conversion functions. + */ +PyObject* valueToPyObject(const value& v); +const value pyObjectToValue(PyObject *o); +PyObject* valuesToPyTuple(const list<value>& v); +const list<value> pyTupleToValues(PyObject* o); + +/** + * Callable python type used to represent a lambda expression. + */ +typedef struct { + PyObject_HEAD + lambda<value(const list<value>&)> func; +} pyLambda; + +void pyLambda_dealloc(PyObject* self) { + PyMem_DEL(self); +} + +PyObject* pyLambda_call(PyObject* self, PyObject* args, unused PyObject* kwds) { + const pyLambda* pyl = (pyLambda*)self; + const value result = pyl->func(pyTupleToValues(args)); + Py_DECREF(args); + PyObject *pyr = valueToPyObject(result); + return pyr; +} + +PyTypeObject pyLambda_type = { + PyObject_HEAD_INIT(0) + 0, + "lambda", + sizeof(pyLambda), + 0, + (destructor)pyLambda_dealloc, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + (ternaryfunc)pyLambda_call, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +/** + * Create a new python object representing a lambda expression. + */ +PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l) { + pyLambda* pyl = NULL; + pyl = PyObject_NEW(pyLambda, &pyLambda_type); + if (pyl != NULL) + pyl->func = l; + return (PyObject *)pyl; +} + +/** + * Convert a list of values to a python list. + */ + +PyObject* valuesToPyListHelper(PyObject* l, const list<value>& v) { + if (isNil(v)) + return l; + PyList_Append(l, valueToPyObject(car(v))); + return valuesToPyListHelper(l, cdr(v)); +} + +PyObject* valuesToPyTuple(const list<value>& v) { + return PyList_AsTuple(valuesToPyListHelper(PyList_New(0), v)); +} + +/** + * Convert a value to a python object. + */ +PyObject* valueToPyObject(const value& v) { + switch (type(v)) { + case value::List: + return valuesToPyTuple(v); + case value::Lambda: + return mkPyLambda(v); + case value::Symbol: + return PyString_FromString(c_str(string("'") + v)); + case value::String: + return PyString_FromString(c_str(v)); + case value::Number: + return PyFloat_FromDouble((double)v); + case value::Bool: + return (bool)v? Py_True : Py_False; + default: + return Py_None; + } +} + +/** + * Convert a python tuple to a list of values. + */ + +const list<value> pyTupleToValuesHelper(PyObject* o, const int i, const int size) { + if (i == size) + return list<value>(); + return cons(pyObjectToValue(PyTuple_GetItem(o, i)), pyTupleToValuesHelper(o, i + 1, size)); +} + +const list<value> pyTupleToValues(PyObject* o) { + return pyTupleToValuesHelper(o, 0, PyTuple_Size(o)); +} + +/** + * Lambda function used to represent a python callable object. + */ +struct pyCallable { + PyObject* func; + + pyCallable(PyObject* func) : func(func) { + Py_INCREF(func); + } + + ~pyCallable() { + Py_DECREF(func); + } + + const value operator()(const list<value>& args) const { + PyObject* pyargs = valuesToPyTuple(args); + PyObject* result = PyObject_CallObject(func, pyargs); + Py_DECREF(pyargs); + const value v = pyObjectToValue(result); + Py_DECREF(result); + return v; + } +}; + +/** + * Convert a python object to a value. + */ +const value pyObjectToValue(PyObject *o) { + if (PyString_Check(o)) { + const char* s = PyString_AsString(o); + if (*s == '\'') + return value(s + 1); + return value(string(s)); + } + if (PyBool_Check(o)) + return value(o == Py_True); + if (PyInt_Check(o)) + return value((double)PyInt_AsLong(o)); + if (PyLong_Check(o)) + return value((double)PyLong_AsLong(o)); + if (PyFloat_Check(o)) + return value((double)PyFloat_AsDouble(o)); + if (PyTuple_Check(o)) + return pyTupleToValues(o); + if (PyCallable_Check(o)) + return lambda<value(const list<value>&)>(pyCallable(o)); + return value(); +} + +/** + * Evaluate an expression against a script provided as a python object. + */ +const failable<value> evalScript(const value& expr, PyObject* script) { + + // Get the requested function + PyObject* func = PyObject_GetAttrString(script, c_str(car<value>(expr))); + if (func == NULL) { + + // The start, stop, and restart functions are optional + const value fn = car<value>(expr); + if (fn == "start" || fn == "stop") { + PyErr_Clear(); + return value(lambda<value(const list<value>&)>()); + } + + return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastError()); + } + if (!PyCallable_Check(func)) { + Py_DECREF(func); + return mkfailure<value>(string("Couldn't find callable function: ") + car<value>(expr)); + } + + // Convert args to python objects + PyObject* args = valuesToPyTuple(cdr<value>(expr)); + + // Call the function + PyObject* result = PyObject_CallObject(func, args); + Py_DECREF(args); + Py_DECREF(func); + if (result == NULL) + return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastError()); + + // Convert python result to a value + const value v = pyObjectToValue(result); + Py_DECREF(result); + return v; +} + +/** + * Read a python script from an input stream. + */ +const failable<PyObject*> readScript(const string& path, istream& is) { + const list<string> ls = streamList(is); + ostringstream os; + write(ls, os); + PyObject* code = Py_CompileStringFlags(c_str(str(os)), c_str(path), Py_file_input, NULL); + if (code == NULL) + return mkfailure<PyObject*>(string("Couldn't compile script: ") + path + " : " + lastError()); + PyObject* mod = PyImport_ExecCodeModule(const_cast<char*>(c_str(path)), code); + if (mod == NULL) + return mkfailure<PyObject*>(string("Couldn't import module: ") + path + " : " + lastError()); + return mod; +} + +/** + * Evaluate an expression against a script provided as an input stream. + */ +const failable<value> evalScript(const value& expr, istream& is) { + failable<PyObject*> script = readScript("script", is); + if (!hasContent(script)) + return mkfailure<value>(reason(script)); + return evalScript(expr, content(script)); +} + +} +} +#endif /* tuscany_python_eval_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/python/mod-python.cpp b/sca-cpp/branches/gcc-4.4/modules/python/mod-python.cpp new file mode 100644 index 0000000000..8561a1fbf4 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/mod-python.cpp @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval Python component implementations. + */ + +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../server/mod-cpp.hpp" +#include "../server/mod-eval.hpp" +#include "mod-python.hpp" + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Apply a lifecycle start or restart event. + */ +const value applyLifecycle(unused const list<value>& params) { + + // Create a Python runtime + new (gc_new<python::PythonRuntime>()) python::PythonRuntime(); + + // Return a nil function as we don't need to handle the stop event + return failable<value>(lambda<value(const list<value>&)>()); +} + +/** + * Evaluate a Python component implementation and convert it to an applicable + * lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) { + const string itype(elementName(impl)); + if (contains(itype, ".python")) + return modpython::evalImplementation(path, impl, px); + if (contains(itype, ".cpp")) + return modcpp::evalImplementation(path, impl, px); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sca-cpp/branches/gcc-4.4/modules/python/mod-python.hpp b/sca-cpp/branches/gcc-4.4/modules/python/mod-python.hpp new file mode 100644 index 0000000000..d13f2227ab --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/mod-python.hpp @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modpython_hpp +#define tuscany_modpython_hpp + +/** + * Evaluation functions used by mod-eval to evaluate Python + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace server { +namespace modpython { + +/** + * Apply a Python component implementation function. + */ +struct applyImplementation { + PyObject* impl; + const list<value> px; + applyImplementation(PyObject* impl, const list<value>& px) : impl(impl), px(px) { + } + const value operator()(const list<value>& params) const { + const value expr = append<value>(params, px); + debug(expr, "modeval::python::applyImplementation::input"); + const failable<value> res = python::evalScript(expr, impl); + const value val = !hasContent(res)? mklist<value>(value(), reason(res)) : mklist<value>(content(res)); + debug(val, "modeval::python::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a Python component implementation and convert it to an applicable + * lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) { + const string fpath(path + attributeValue("script", impl)); + ifstream is(fpath); + if (fail(is)) + return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath); + const failable<PyObject*> script = python::readScript(fpath, is); + if (!hasContent(script)) + return mkfailure<lambda<value(const list<value>&)> >(reason(script)); + return lambda<value(const list<value>&)>(applyImplementation(content(script), px)); +} + +} +} +} + +#endif /* tuscany_modpython_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/python/python-conf b/sca-cpp/branches/gcc-4.4/modules/python/python-conf new file mode 100755 index 0000000000..983679db4d --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/python-conf @@ -0,0 +1,28 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a Python server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +cat >>$root/conf/httpd.conf <<EOF +# Support for Python SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_python.so + +EOF diff --git a/sca-cpp/branches/gcc-4.4/modules/python/python-shell.cpp b/sca-cpp/branches/gcc-4.4/modules/python/python-shell.cpp new file mode 100644 index 0000000000..89b47b8d44 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/python-shell.cpp @@ -0,0 +1,40 @@ +/* + * 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$ */ + +/** + * Python script evaluator shell, used for interactive testing of scripts. + */ + +#include <assert.h> +#include "gc.hpp" +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +int main(const int argc, char** argv) { + tuscany::gc_scoped_pool pool; + if (argc != 2) { + tuscany::cerr << "Usage: python-shell <script.py>" << tuscany::endl; + return 1; + } + tuscany::python::evalDriverRun(argv[1], tuscany::cin, tuscany::cout); + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/python/python-test.cpp b/sca-cpp/branches/gcc-4.4/modules/python/python-test.cpp new file mode 100644 index 0000000000..c4ffcee57e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/python-test.cpp @@ -0,0 +1,109 @@ +/* + * 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 Python script evaluator. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +namespace tuscany { +namespace python { + +const string testPythonAdd = + "def add(x, y):\n" + " return x + y\n"; + +bool testEvalExpr() { + gc_scoped_pool pool; + PythonRuntime py; + + istringstream is(testPythonAdd); + failable<PyObject*> script = readScript("script", is); + assert(hasContent(script)); + + const value exp = mklist<value>("add", 2, 3); + const failable<value> r = evalScript(exp, content(script)); + assert(hasContent(r)); + assert(content(r) == value(5)); + + return true; +} + +const value mult(const list<value>& args) { + const double x = car(args); + const double y = cadr(args); + return x * y; +} + +const string testReturnLambda( + "def mul(x, y):\n" + " return x * y\n" + "\n" + "def testReturnLambda():\n" + " return mul\n"); + +const string testCallLambda( + "def testCallLambda(l, x, y):\n" + " return l(x, y)\n"); + +bool testEvalLambda() { + gc_scoped_pool pool; + PythonRuntime py; + + const value trl = mklist<value>("testReturnLambda"); + istringstream trlis(testReturnLambda); + const failable<value> trlv = evalScript(trl, trlis); + + assert(hasContent(trlv)); + assert(isLambda(content(trlv))); + const lambda<value(const list<value>&)> trll(content(trlv)); + assert(trll(mklist<value>(2, 3)) == value(6)); + + istringstream tclis(testCallLambda); + const value tcl = mklist<value>("testCallLambda", content(trlv), 2, 3); + const failable<value> tclv = evalScript(tcl, tclis); + assert(hasContent(tclv)); + assert(content(tclv) == value(6)); + + istringstream tcelis(testCallLambda); + const value tcel = mklist<value>("testCallLambda", lambda<value(const list<value>&)>(mult), 3, 4); + const failable<value> tcelv = evalScript(tcel, tcelis); + assert(hasContent(tcelv)); + assert(content(tcelv) == value(12)); + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::python::testEvalExpr(); + tuscany::python::testEvalLambda(); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/python/server-test b/sca-cpp/branches/gcc-4.4/modules/python/server-test new file mode 100755 index 0000000000..b01f5f501d --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/server-test @@ -0,0 +1,39 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +../http/httpd-conf tmp localhost 8090 ../server/htdocs +../server/server-conf tmp +./python-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +../http/httpd-stop tmp +sleep 2 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/python/server-test.py b/sca-cpp/branches/gcc-4.4/modules/python/server-test.py new file mode 100644 index 0000000000..dcda763043 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/server-test.py @@ -0,0 +1,42 @@ +# 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. + +# JSON-RPC test case + +def echo(x): + return x + +# ATOMPub test case + +def get(id): + if id == (): + return ("Sample Feed", "123456789", + ("Item", "111", (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99))), + ("Item", "222", (("'javaClass", "services.Item"), ("'name", "Orange"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 3.55))), + ("Item", "333", (("'javaClass", "services.Item"), ("name", "Pear"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 1.55)))) + + entry = (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99)) + return ("Item", id[0], entry) + +def post(collection, item): + return ("123456789",) + +def put(id, item): + return True + +def delete(id): + return True diff --git a/sca-cpp/branches/gcc-4.4/modules/python/tuscany-sca-1.1-implementation-python.xsd b/sca-cpp/branches/gcc-4.4/modules/python/tuscany-sca-1.1-implementation-python.xsd new file mode 100644 index 0000000000..c811ba6729 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/tuscany-sca-1.1-implementation-python.xsd @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<schema xmlns="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://tuscany.apache.org/xmlns/sca/1.1" + xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + elementFormDefault="qualified"> + + <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200912" schemaLocation="sca-1.1-cd04.xsd"/> + + <element name="implementation.python" type="t:PythonImplementation" substitutionGroup="sca:implementation"/> + + <complexType name="PythonImplementation"> + <complexContent> + <extension base="sca:Implementation"> + <sequence> + <any namespace="##targetNamespace" processContents="lax" + minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="script" type="anyURI" use="required"/> + <anyAttribute namespace="##any" processContents="lax"/> + </extension> + </complexContent> + </complexType> + +</schema> diff --git a/sca-cpp/branches/gcc-4.4/modules/python/wiring-test b/sca-cpp/branches/gcc-4.4/modules/python/wiring-test new file mode 100755 index 0000000000..42df9d4ff3 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/python/wiring-test @@ -0,0 +1,78 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 ../server/htdocs +../server/server-conf tmp +./python-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html ../server/htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml ../server/htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml ../server/htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @../server/htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt ../server/htdocs/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/scdl/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/scdl/Makefile.am new file mode 100644 index 0000000000..542ce4be20 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scdl/Makefile.am @@ -0,0 +1,22 @@ +# 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. + +scdl_test_SOURCES = scdl-test.cpp +scdl_test_LDFLAGS = -lxml2 + +noinst_PROGRAMS = scdl-test +TESTS = scdl-test diff --git a/sca-cpp/branches/gcc-4.4/modules/scdl/scdl-test.cpp b/sca-cpp/branches/gcc-4.4/modules/scdl/scdl-test.cpp new file mode 100644 index 0000000000..e8ee77eb4e --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scdl/scdl-test.cpp @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test SCDL read functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "scdl.hpp" + +namespace tuscany { +namespace scdl { + +bool testComposite() { + ifstream is("test.composite"); + const list<value> c = readXML(streamList(is)); + return true; +} + +bool testComponents() { + ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + assert(length(c) == 4); + + const value store = car(c); + assert(name(store) == string("Store")); + const value impl = implementation(store); + assert(uri(impl) == string("store.html")); + assert(implementationType(impl) == "t:implementation.scheme"); + + const value catalog = named(string("Catalog"), c); + assert(name(catalog) == string("Catalog")); + + const list<value> t = mkbtree(sort(nameToElementAssoc(c))); + assert(assoctree<value>("Catalog", t) == mklist<value>("Catalog" , cadr(c))); + return true; +} + +bool testServices() { + ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value store = car(c); + + assert(length(services(store)) == 1); + const value widget = car(services(store)); + assert(name(widget) == string("Widget")); + + assert(length(bindings(widget)) == 1); + const value binding = car(bindings(widget)); + assert(uri(binding) == string("/store")); + assert(bindingType(binding) == "t:binding.http"); + return true; +} + +bool testReferences() { + ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value store = car(c); + + assert(length(references(store)) == 3); + const value catalog = car(references(store)); + assert(name(catalog) == string("catalog")); + assert(target(catalog) == string("Catalog")); + + assert(length(bindings(catalog)) == 1); + const value binding = car(bindings(catalog)); + assert(uri(binding) == value()); + assert(bindingType(binding) == "t:binding.jsonrpc"); + + const list<value> t = mkbtree(sort(referenceToTargetAssoc(references(store)))); + assert(assoctree<value>("shoppingCart", t) == mklist<value>(string("shoppingCart"), string("ShoppingCart/Cart"))); + return true; +} + +bool testProperties() { + ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value catalog = named(string("Catalog"), c); + + assert(length(properties(catalog)) == 1); + const value currencyCode = car(properties(catalog)); + assert(name(currencyCode) == string("currencyCode")); + assert(propertyValue(currencyCode) == string("USD")); + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::scdl::testComposite(); + tuscany::scdl::testComponents(); + tuscany::scdl::testServices(); + tuscany::scdl::testReferences(); + tuscany::scdl::testProperties(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/scdl/scdl.hpp b/sca-cpp/branches/gcc-4.4/modules/scdl/scdl.hpp new file mode 100644 index 0000000000..531144e219 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scdl/scdl.hpp @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scdl_hpp +#define tuscany_scdl_hpp + +/** + * SCDL read functions. + */ + +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "xml.hpp" + +namespace tuscany { +namespace scdl { + +/** + * Returns a list of components in a composite. + */ +const list<value> components(const value& l) { + const list<value> cs = elementChildren("composite", l); + if (isNil(cs)) + return cs; + return elementChildren("component", car(cs)); +} + +/** + * Returns the name of a component, service or reference. + */ +const value name(const value& l) { + return attributeValue("name", l); +} + +/** + * Convert a list of elements to a name -> element assoc list. + */ +const list<value> nameToElementAssoc(const list<value>& l) { + if (isNil(l)) + return l; + const value e(car(l)); + return cons<value>(mklist<value>(name(e), e), nameToElementAssoc(cdr(l))); +} + +/** + * Returns the scdl declaration with the given name. + */ +struct filterName { + const value n; + filterName(const value& n) : n(n) { + } + const bool operator()(const value& v) const { + return name(v) == n; + } +}; + +const value named(const value& name, const value& l) { + const list<value> c = filter<value>(filterName(name), l); + if (isNil(c)) + return value(); + return car(c); +} + +/** + * Returns the implementation of a component. + */ +const bool filterImplementation(const value& v) { + return isElement(v) && contains(string(cadr<value>(v)), "implementation."); +} + +const value implementation(const value& l) { + const list<value> n = filter<value>(filterImplementation, l); + if (isNil(n)) + return value(); + return car(n); +} + +/** + * Returns the URI of a service, reference or implementation. + */ +const value uri(const value& l) { + return attributeValue("uri", l); +} + +/** + * Returns a list of services in a component. + */ +const list<value> services(const value& l) { + return elementChildren("service", l); +} + +/** + * Returns a list of references in a component. + */ +const list<value> references(const value& l) { + return elementChildren("reference", l); +} + +/** + * Returns the target of a reference. + */ +const value target(const value& l) { + return attributeValue("target", l); +} + +/** + * Convert a list of references to a reference name -> target assoc list. + */ +const list<value> referenceToTargetAssoc(const list<value>& r) { + if (isNil(r)) + return r; + const value ref(car(r)); + return cons<value>(mklist<value>(scdl::name(ref), scdl::target(ref)), referenceToTargetAssoc(cdr(r))); +} + +/** + * Returns a list of bindings in a service or reference. + */ +const bool filterBinding(const value& v) { + return isElement(v) && contains(string(cadr<value>(v)), "binding."); +} + +const list<value> bindings(const value& l) { + return filter<value>(filterBinding, l); +} + +/** + * Returns a list of properties in a component. + */ +const list<value> properties(const value& l) { + return elementChildren("property", l); +} + +/** + * Returns the type of an implementation. + */ +const value implementationType(const value& l) { + return elementName(l); +} + +/** + * Returns the type of a binding. + */ +const value bindingType(const value& l) { + return elementName(l); +} + +/** + * Returns the value of a property. + */ +const value propertyValue(const value& l) { + return elementValue(l); +} + +} +} + +#endif /* tuscany_scdl_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scdl/test.composite b/sca-cpp/branches/gcc-4.4/modules/scdl/test.composite new file mode 100644 index 0000000000..f6fdba7f5f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scdl/test.composite @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://store" + name="store"> + + <component name="Store"> + <t:implementation.scheme script="store.scm"/> + <service name="Widget"> + <t:binding.http uri="/store"/> + </service> + <reference name="catalog" target="Catalog"> + <t:binding.jsonrpc/> + </reference> + <reference name="shoppingCart" target="ShoppingCart/Cart"> + <t:binding.atom/> + </reference> + <reference name="shoppingTotal" target="ShoppingCart/Total"> + <t:binding.jsonrpc/> + </reference> + </component> + + <component name="Catalog"> + <t:implementation.scheme script="fruits-catalog.scm"/> + <property name="currencyCode">USD</property> + <service name="Catalog"> + <t:binding.jsonrpc/> + </service> + <reference name="currencyConverter" target="CurrencyConverter"/> + </component> + + <component name="ShoppingCart"> + <t:implementation.scheme script="shopping-cart.scm"/> + <service name="Cart"> + <t:binding.atom uri="/ShoppingCart"/> + </service> + <service name="Total"> + <t:binding.jsonrpc/> + </service> + </component> + + <component name="CurrencyConverter"> + <t:implementation.scheme script="currency-converter.scm"/> + </component> + +</composite> diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/scheme/Makefile.am new file mode 100644 index 0000000000..03556d0c45 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/Makefile.am @@ -0,0 +1,26 @@ +# 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. + +xsddir=$(prefix)/xsd +nobase_xsd_DATA = *.xsd + +eval_test_SOURCES = eval-test.cpp + +eval_shell_SOURCES = eval-shell.cpp + +noinst_PROGRAMS = eval-test eval-shell +TESTS = eval-test diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/driver.hpp b/sca-cpp/branches/gcc-4.4/modules/scheme/driver.hpp new file mode 100644 index 0000000000..112c226ed1 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/driver.hpp @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scheme_driver_hpp +#define tuscany_scheme_driver_hpp + +/** + * Script evaluator main driver loop. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +const string evalOutputPrompt("; "); +const string evalInputPrompt("=> "); + +const bool promptForInput(const string& str, ostream& out) { + out << endl << endl << str; + return true; +} + +const bool announceOutput(const string str, ostream& out) { + out << endl << str; + return true; +} + +const bool userPrint(const value val, ostream& out) { + if(isCompoundProcedure(val)) + writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out); + writeValue(val, out); + return true; +} + +const value evalDriverLoop(istream& in, ostream& out, Env& env) { + promptForInput(evalInputPrompt, out); + value input = readValue(in); + if (isNil(input)) + return input; + const value output = evalExpr(input, env); + announceOutput(evalOutputPrompt, out); + userPrint(output, out); + return evalDriverLoop(in, out, env); +} + +const bool evalDriverRun(istream& in, ostream& out) { + setupDisplay(out); + Env env = setupEnvironment(); + evalDriverLoop(in, out, env); + return true; +} + +} +} +#endif /* tuscany_scheme_driver_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/environment.hpp b/sca-cpp/branches/gcc-4.4/modules/scheme/environment.hpp new file mode 100644 index 0000000000..bfb415a978 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/environment.hpp @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scheme_environment_hpp +#define tuscany_scheme_environment_hpp + +/** + * Script evaluator environment implementation. + */ + +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" +#include <string> + +namespace tuscany { +namespace scheme { + +typedef value Frame; +typedef list<value> Env; + +const value trueSymbol("true"); +const value falseSymbol("false"); +const value defineSymbol("define"); +const value setSymbol("set!"); +const value dotSymbol("."); + +const Env theEmptyEnvironment() { + return list<value>(); +} + +const bool isDefinition(const value& exp) { + return isTaggedList(exp, defineSymbol); +} + +const bool isAssignment(const value& exp) { + return isTaggedList(exp, setSymbol); +} + +const bool isVariable(const value& exp) { + return isSymbol(exp); +} + +const Env enclosingEnvironment(const Env& env) { + return cdr(env); +} + +const gc_ptr<Frame> firstFrame(const Env& env) { + return car(env); +} + +list<value> frameVariables(const Frame& frame) { + return car((list<value> )frame); +} + +list<value> frameValues(const Frame& frame) { + return cdr((list<value> )frame); +} + +const bool isDotVariable(const value& var) { + return var == dotSymbol; +} + +const Frame makeBinding(const Frame& frameSoFar, const list<value>& variables, const list<value> values) { + if (isNil(variables)) { + if (!isNil(values)) + logStream() << "Too many arguments supplied " << values << endl; + return frameSoFar; + } + if (isDotVariable(car(variables))) + return makeBinding(frameSoFar, cdr(variables), mklist<value>(values)); + + if (isNil(values)) { + if (!isNil(variables)) + logStream() << "Too few arguments supplied " << variables << endl; + return frameSoFar; + } + + const list<value> vars = cons(car(variables), frameVariables(frameSoFar)); + const list<value> vals = cons(car(values), frameValues(frameSoFar)); + const Frame newFrame = cons(value(vars), vals); + + return makeBinding(newFrame, cdr(variables), cdr(values)); +} + +const gc_ptr<Frame> makeFrame(const list<value>& variables, const list<value> values) { + gc_ptr<Frame> frame = new (gc_new<Frame>()) Frame(); + *frame = value(makeBinding(cons(value(list<value>()), list<value>()), variables, values)); + return frame; +} + +const value definitionVariable(const value& exp) { + const list<value> exps(exp); + if(isSymbol(car(cdr(exps)))) + return car(cdr(exps)); + const list<value> lexps(car(cdr(exps))); + return car(lexps); +} + +const value definitionValue(const value& exp) { + const list<value> exps(exp); + if(isSymbol(car(cdr(exps)))) + return car(cdr(cdr(exps))); + const list<value> lexps(car(cdr(exps))); + return makeLambda(cdr(lexps), cdr(cdr(exps))); +} + +const value assignmentVariable(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value assignmentValue(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const Frame addBindingToFrame(const value& var, const value& val, const Frame& frame) { + return cons(value(cons(var, frameVariables(frame))), cons(val, frameValues(frame))); +} + +const bool defineVariable(const value& var, const value& val, Env& env) { + *firstFrame(env) = addBindingToFrame(var, val, *firstFrame(env)); + return true; +} + +const Env extendEnvironment(const list<value>& vars, const list<value>& vals, const Env& baseEnv) { + return cons<value>(makeFrame(vars, vals), baseEnv); +} + +const Env setupEnvironment() { + Env env = extendEnvironment(primitiveProcedureNames(), primitiveProcedureObjects(), theEmptyEnvironment()); + defineVariable(trueSymbol, true, env); + defineVariable(falseSymbol, false, env); + return env; +} + +const value lookupEnvLoop(const value& var, const Env& env); + +const value lookupEnvScan(const value& var, const list<value>& vars, const list<value>& vals, const Env& env) { + if(isNil(vars)) + return lookupEnvLoop(var, enclosingEnvironment(env)); + if(var == car(vars)) + return car(vals); + return lookupEnvScan(var, cdr(vars), cdr(vals), env); +} + +const value lookupEnvLoop(const value& var, const Env& env) { + if(env == theEmptyEnvironment()) { + logStream() << "Unbound variable " << var << endl; + return value(); + } + return lookupEnvScan(var, frameVariables(*firstFrame(env)), frameValues(*firstFrame(env)), env); +} + +const value lookupVariableValue(const value& var, const Env& env) { + return lookupEnvLoop(var, env); +} + +} +} +#endif /* tuscany_scheme_environment_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/eval-shell.cpp b/sca-cpp/branches/gcc-4.4/modules/scheme/eval-shell.cpp new file mode 100644 index 0000000000..4aa67c2375 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/eval-shell.cpp @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Script evaluator shell, used for interactive testing of scripts. + */ + +#include <assert.h> +#include "gc.hpp" +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +int main() { + tuscany::gc_scoped_pool pool; + tuscany::scheme::evalDriverRun(tuscany::cin, tuscany::cout); + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/eval-test.cpp b/sca-cpp/branches/gcc-4.4/modules/scheme/eval-test.cpp new file mode 100644 index 0000000000..7c4c0c69c4 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/eval-test.cpp @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test script evaluator. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" + +namespace tuscany { +namespace scheme { + +bool testEnv() { + gc_scoped_pool pool; + Env globalEnv = list<value>(); + Env env = extendEnvironment(mklist<value>("a"), mklist<value>(1), globalEnv); + defineVariable("x", env, env); + assert(lookupVariableValue(value("x"), env) == env); + assert(lookupVariableValue("a", env) == value(1)); + return true; +} + +bool testEnvGC() { + resetLambdaCounters(); + resetListCounters(); + resetValueCounters(); + testEnv(); + assert(checkValueCounters()); + assert(checkLambdaCounters()); + assert(checkListCounters()); + return true; +} + +bool testRead() { + istringstream is("abcd"); + assert(readValue(is) == "abcd"); + + istringstream is2("123"); + assert(readValue(is2) == value(123)); + + istringstream is3("(abcd)"); + assert(readValue(is3) == mklist(value("abcd"))); + + istringstream is4("(abcd xyz)"); + assert(readValue(is4) == mklist<value>("abcd", "xyz")); + + istringstream is5("(abcd (xyz tuv))"); + assert(readValue(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv"))); + + return true; +} + +bool testWrite() { + const list<value> i = list<value>() + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + + (list<value>() + "item" + + (list<value>() + "name" + "Apple") + + (list<value>() + "price" + "$2.99"))) + + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + + (list<value>() + "item" + + (list<value>() + "name" + "Orange") + + (list<value>() + "price" + "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i)); + ostringstream os; + writeValue(a, os); + istringstream is(str(os)); + assert(readValue(is) == a); + return true; +} + +const string testSchemeNumber( + "(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) " + "(testNumber)"); + +const string testSchemeString( + "(define (testString) (if (= \"abc\" \"abc\") (display \"testString ok\") (error \"testString\"))) " + "(testString)"); + +const string testSchemeDefinition( + "(define a \"abc\") (define (testDefinition) (if (= a \"abc\") (display \"testDefinition ok\") (error \"testDefinition\"))) " + "(testDefinition)"); + +const string testSchemeIf( + "(define (testIf) (if (= \"abc\" \"abc\") (if (= \"xyz\" \"xyz\") (display \"testIf ok\") (error \"testNestedIf\")) (error \"testIf\"))) " + "(testIf)"); + +const string testSchemeCond( + "(define (testCond) (cond ((= \"abc\" \"abc\") (display \"testCond ok\")) (else (error \"testIf\"))))" + "(testCond)"); + +const string testSchemeBegin( + "(define (testBegin) " + "(begin " + "(define a \"abc\") " + "(if (= a \"abc\") (display \"testBegin1 ok\") (error \"testBegin\")) " + "(define x \"xyz\") " + "(if (= x \"xyz\") (display \"testBegin2 ok\") (error \"testBegin\")) " + ") " + ") " + "(testBegin)"); + +const string testSchemeLambda( + "(define sqrt (lambda (x) (* x x))) " + "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testLambda ok\") (error \"testLambda\"))) " + "(testLambda)"); + +const string testSchemeForward( + "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testForward ok\") (error \"testForward\"))) " + "(define sqrt (lambda (x) (* x x))) " + "(testLambda)"); + +const string evalOutput(const string& scm) { + istringstream is(scm); + ostringstream os; + evalDriverRun(is, os); + return str(os); +} + +bool testEval() { + gc_scoped_pool pool; + assert(contains(evalOutput(testSchemeNumber), "testNumber ok")); + assert(contains(evalOutput(testSchemeString), "testString ok")); + assert(contains(evalOutput(testSchemeDefinition), "testDefinition ok")); + assert(contains(evalOutput(testSchemeIf), "testIf ok")); + assert(contains(evalOutput(testSchemeCond), "testCond ok")); + assert(contains(evalOutput(testSchemeBegin), "testBegin1 ok")); + assert(contains(evalOutput(testSchemeBegin), "testBegin2 ok")); + assert(contains(evalOutput(testSchemeLambda), "testLambda ok")); + assert(contains(evalOutput(testSchemeForward), "testForward ok")); + return true; +} + +bool testEvalExpr() { + gc_scoped_pool pool; + const value exp = mklist<value>("+", 2, 3); + Env env = setupEnvironment(); + const value r = evalExpr(exp, env); + assert(r == value(5)); + return true; +} + +bool testEvalRun() { + gc_scoped_pool pool; + evalDriverRun(cin, cout); + return true; +} + +const value mult(const list<value>& args) { + const double x = car(args); + const double y = cadr(args); + return x * y; +} + +const string testReturnLambda( + "(define (testReturnLambda) * )"); + +const string testCallLambda( + "(define (testCallLambda l x y) (l x y))"); + +bool testEvalLambda() { + gc_scoped_pool pool; + Env env = setupEnvironment(); + + const value trl = mklist<value>("testReturnLambda"); + istringstream trlis(testReturnLambda); + const value trlv = evalScript(trl, trlis, env); + + istringstream tclis(testCallLambda); + const value tcl = cons<value>("testCallLambda", quotedParameters(mklist<value>(trlv, 2, 3))); + const value tclv = evalScript(tcl, tclis, env); + assert(tclv == value(6)); + + istringstream tcelis(testCallLambda); + const value tcel = cons<value>("testCallLambda", quotedParameters(mklist<value>(primitiveProcedure(mult), 3, 4))); + const value tcelv = evalScript(tcel, tcelis, env); + assert(tcelv == value(12)); + return true; +} + +bool testEvalGC() { + resetLambdaCounters(); + resetListCounters(); + resetValueCounters(); + testEval(); + testEvalExpr(); + testEvalLambda(); + assert(checkValueCounters()); + assert(checkLambdaCounters()); + assert(checkListCounters()); + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::scheme::testEnv(); + tuscany::scheme::testEnvGC(); + tuscany::scheme::testRead(); + tuscany::scheme::testWrite(); + tuscany::scheme::testEval(); + tuscany::scheme::testEvalExpr(); + tuscany::scheme::testEvalLambda(); + tuscany::scheme::testEvalGC(); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/eval.hpp b/sca-cpp/branches/gcc-4.4/modules/scheme/eval.hpp new file mode 100644 index 0000000000..34d1a7bc17 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/eval.hpp @@ -0,0 +1,290 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scheme_eval_hpp +#define tuscany_scheme_eval_hpp + +/** + * Core script evaluation logic. + */ + +#include <string.h> +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" +#include "io.hpp" +#include "environment.hpp" + +namespace tuscany { +namespace scheme { + +const value evalExpr(const value& exp, Env& env); + +const value compoundProcedureSymbol("compound-procedure"); +const value procedureSymbol("procedure"); +const value applySymbol("apply"); +const value beginSymbol("begin"); +const value condSymbol("cond"); +const value elseSymbol("else"); +const value ifSymbol("if"); + +const bool isBegin(const value& exp) { + return isTaggedList(exp, beginSymbol); +} + +const list<value> beginActions(const value& exp) { + return cdr((list<value> )exp); +} + +const bool isLambdaExpr(const value& exp) { + return isTaggedList(exp, lambdaSymbol); +} + +const list<value> lambdaParameters(const value& exp) { + return car(cdr((list<value> )exp)); +} + +static list<value> lambdaBody(const value& exp) { + return cdr(cdr((list<value> )exp)); +} + +const value makeProcedure(const list<value>& parameters, const value& body, const Env& env) { + return mklist<value>(procedureSymbol, parameters, body, env); +} + +const bool isApply(const value& exp) { + return isTaggedList(exp, applySymbol); +} + +const bool isApplication(const value& exp) { + return isList(exp); +} + +const value operat(const value& exp) { + return car((list<value> )exp); +} + +const list<value> operands(const value& exp) { + return cdr((list<value> )exp); +} + +const list<value> listOfValues(const list<value> exps, Env& env) { + if(isNil(exps)) + return list<value> (); + return cons(evalExpr(car(exps), env), listOfValues(cdr(exps), env)); +} + +const value applyOperat(const value& exp) { + return cadr((list<value> )exp); +} + +const value applyOperand(const value& exp) { + return caddr((list<value> )exp); +} + +const bool isCompoundProcedure(const value& procedure) { + return isTaggedList(procedure, procedureSymbol); +} + +const list<value> procedureParameters(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value procedureBody(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const Env procedureEnvironment(const value& exp) { + return (Env)car(cdr(cdr(cdr((list<value> )exp)))); +} + +const bool isLastExp(const list<value>& seq) { + return isNil(cdr(seq)); +} + +const value firstExp(const list<value>& seq) { + return car(seq); +} + +const list<value> restExp(const list<value>& seq) { + return cdr(seq); +} + +const value makeBegin(const list<value> seq) { + return cons(beginSymbol, seq); +} + +const value evalSequence(const list<value>& exps, Env& env) { + if(isLastExp(exps)) + return evalExpr(firstExp(exps), env); + evalExpr(firstExp(exps), env); + return evalSequence(restExp(exps), env); +} + +const value applyProcedure(const value& procedure, list<value>& arguments) { + if(isPrimitiveProcedure(procedure)) + return applyPrimitiveProcedure(procedure, arguments); + if(isCompoundProcedure(procedure)) { + Env env = extendEnvironment(procedureParameters(procedure), arguments, procedureEnvironment(procedure)); + return evalSequence(procedureBody(procedure), env); + } + logStream() << "Unknown procedure type " << procedure << endl; + return value(); +} + +const value sequenceToExp(const list<value> exps) { + if(isNil(exps)) + return exps; + if(isLastExp(exps)) + return firstExp(exps); + return makeBegin(exps); +} + +const list<value> condClauses(const value& exp) { + return cdr((list<value> )exp); +} + +const value condPredicate(const value& clause) { + return car((list<value> )clause); +} + +const list<value> condActions(const value& clause) { + return cdr((list<value> )clause); +} + +const value ifPredicate(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value ifConsequent(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const value ifAlternative(const value& exp) { + if(!isNil(cdr(cdr(cdr((list<value> )exp))))) + return car(cdr(cdr(cdr((list<value> )exp)))); + return false; +} + +const bool isCond(const value& exp) { + return isTaggedList(exp, condSymbol); +} + +const bool isCondElseClause(const value& clause) { + return condPredicate(clause) == elseSymbol; +} + +const bool isIf(const value& exp) { + return isTaggedList(exp, ifSymbol); +} + +const value makeIf(value predicate, value consequent, value alternative) { + return mklist(ifSymbol, predicate, consequent, alternative); +} + +const value expandClauses(const list<value>& clauses) { + if(isNil(clauses)) + return false; + const value first = car(clauses); + const list<value> rest = cdr(clauses); + if(isCondElseClause(first)) { + if(isNil(rest)) + return sequenceToExp(condActions(first)); + logStream() << "else clause isn't last " << clauses << endl; + return value(); + } + return makeIf(condPredicate(first), sequenceToExp(condActions(first)), expandClauses(rest)); +} + +value condToIf(const value& exp) { + return expandClauses(condClauses(exp)); +} + +value evalIf(const value& exp, Env& env) { + if(isTrue(evalExpr(ifPredicate(exp), env))) + return evalExpr(ifConsequent(exp), env); + return evalExpr(ifAlternative(exp), env); +} + +const value evalDefinition(const value& exp, Env& env) { + defineVariable(definitionVariable(exp), evalExpr(definitionValue(exp), env), env); + return definitionVariable(exp); +} + +const value evalExpr(const value& exp, Env& env) { + if(isSelfEvaluating(exp)) + return exp; + if(isQuoted(exp)) + return textOfQuotation(exp); + if(isDefinition(exp)) + return evalDefinition(exp, env); + if(isIf(exp)) + return evalIf(exp, env); + if(isBegin(exp)) + return evalSequence(beginActions(exp), env); + if(isCond(exp)) + return evalExpr(condToIf(exp), env); + if(isLambdaExpr(exp)) + return makeProcedure(lambdaParameters(exp), lambdaBody(exp), env); + if(isVariable(exp)) + return lookupVariableValue(exp, env); + if(isApply(exp)) { + list<value> applyOperandValues = evalExpr(applyOperand(exp), env); + return applyProcedure(evalExpr(applyOperat(exp), env), applyOperandValues); + } + if(isApplication(exp)) { + list<value> operandValues = listOfValues(operands(exp), env); + return applyProcedure(evalExpr(operat(exp), env), operandValues); + } + logStream() << "Unknown expression type " << exp << endl; + return value(); +} + +const list<value> quotedParameters(const list<value>& p) { + if (isNil(p)) + return p; + return cons<value>(mklist<value>(quoteSymbol, car(p)), quotedParameters(cdr(p))); +} + +/** + * Evaluate an expression against a script provided as a list of values. + */ +const value evalScriptLoop(const value& expr, const list<value>& script, scheme::Env& env) { + if (isNil(script)) + return scheme::evalExpr(expr, env); + scheme::evalExpr(car(script), env); + return evalScriptLoop(expr, cdr(script), env); +} + +const value evalScript(const value& expr, const value& script, Env& env) { + return evalScriptLoop(expr, script, env); +} + +/** + * Evaluate an expression against a script provided as an input stream. + */ +const value evalScript(const value& expr, istream& is, Env& env) { + return evalScript(expr, readScript(is), env); +} + +} +} +#endif /* tuscany_scheme_eval_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/io.hpp b/sca-cpp/branches/gcc-4.4/modules/scheme/io.hpp new file mode 100644 index 0000000000..5e5397cfeb --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/io.hpp @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scheme_io_hpp +#define tuscany_scheme_io_hpp + +/** + * Script evaluator IO functions. + */ + +#include <ctype.h> +#include "stream.hpp" +#include "string.hpp" + +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" + +namespace tuscany { +namespace scheme { + +const value rightParenthesis(mklist<value>(")")); +const value leftParenthesis(mklist<value>("(")); +const value comment(mklist<value>(";")); + +const double stringToNumber(const string& str) { + return atof(c_str(str)); +} + +const bool isWhitespace(const char ch) { + return ch != -1 && isspace(ch); +} + +const bool isIdentifierStart(const char ch) { + return ch != -1 && !isspace(ch) && !isdigit(ch); +} + +const bool isIdentifierPart(const char ch) { + return ch != -1 && !isspace(ch) && ch != '(' && ch != ')'; +} + +const bool isDigit(const char ch) { + return isdigit(ch) || ch == '.'; +} + +const bool isLeftParenthesis(const value& token) { + return leftParenthesis == token; +} + +const bool isRightParenthesis(const value& token) { + return rightParenthesis == token; +} + +const char readChar(istream& in) { + if(in.eof()) { + return -1; + } + char c = (char)get(in); + return c; +} + +const char peekChar(istream& in) { + if(eof(in)) + return -1; + char c = (char)peek(in); + return c; +} + +const bool isQuote(const value& token) { + return token == quoteSymbol; +} + +const value skipComment(istream& in); +const value readQuoted(istream& in); +const value readIdentifier(const char chr, istream& in); +const value readString(istream& in); +const value readNumber(const char chr, istream& in); +const value readValue(istream& in); + +const value readToken(istream& in) { + const char firstChar = readChar(in); + if(isWhitespace(firstChar)) + return readToken(in); + if(firstChar == ';') + return skipComment(in); + if(firstChar == '\'') + return readQuoted(in); + if(firstChar == '(') + return leftParenthesis; + if(firstChar == ')') + return rightParenthesis; + if(firstChar == '"') + return readString(in); + if(isIdentifierStart(firstChar)) + return readIdentifier(firstChar, in); + if(isDigit(firstChar)) + return readNumber(firstChar, in); + if(firstChar == -1) + return value(); + logStream() << "Illegal lexical syntax '" << firstChar << "'" << endl; + return readToken(in); +} + +const value skipComment(istream& in) { + const char nextChar = readChar(in); + if (nextChar == '\n') + return readToken(in); + return skipComment(in); +} + +const value readQuoted(istream& in) { + return mklist(quoteSymbol, readValue(in)); +} + +const list<value> readList(const list<value>& listSoFar, istream& in) { + const value token = readToken(in); + if(isNil(token) || isRightParenthesis(token)) + return reverse(listSoFar); + if(isLeftParenthesis(token)) + return readList(cons(value(readList(list<value> (), in)), listSoFar), in); + return readList(cons(token, listSoFar), in); +} + +const string listToString(const list<char>& l) { + if(isNil(l)) + return ""; + const char buf[1] = { car(l) }; + return string(buf, 1) + listToString(cdr(l)); +} + +const list<char> readIdentifierHelper(const list<char>& listSoFar, istream& in) { + const char nextChar = peekChar(in); + if(isIdentifierPart(nextChar)) + return readIdentifierHelper(cons(readChar(in), listSoFar), in); + return reverse(listSoFar); +} + +const value readIdentifier(const char chr, istream& in) { + return c_str(listToString(readIdentifierHelper(mklist(chr), in))); +} + +const list<char> readStringHelper(const list<char>& listSoFar, istream& in) { + const char nextChar = readChar(in); + if(nextChar != -1 && nextChar != '"') + return readStringHelper(cons(nextChar, listSoFar), in); + return reverse(listSoFar); +} + +const value readString(istream& in) { + return listToString(readStringHelper(list<char>(), in)); +} + +const list<char> readNumberHelper(const list<char>& listSoFar, istream& in) { + const char nextChar = peekChar(in); + if(isDigit(nextChar)) + return readNumberHelper(cons(readChar(in), listSoFar), in); + return reverse(listSoFar); +} + +const value readNumber(const char chr, istream& in) { + return stringToNumber(listToString(readNumberHelper(mklist(chr), in))); +} + +const value readValue(istream& in) { + const value nextToken = readToken(in); + if(isLeftParenthesis(nextToken)) + return readList(list<value> (), in); + return nextToken; +} + +const value readValue(const string s) { + istringstream in(s); + const value nextToken = readToken(in); + if(isLeftParenthesis(nextToken)) + return readList(list<value> (), in); + return nextToken; +} + +const bool writeValue(const value& val, ostream& out) { + out << val; + return true; +} + +const string writeValue(const value& val) { + ostringstream out; + out << val; + return str(out); +} + +const value readScript(istream& in) { + const value val = readValue(in); + if (isNil(val)) + return list<value>(); + return cons(val, (list<value>)readScript(in)); +} + +} +} +#endif /* tuscany_scheme_io_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/primitive.hpp b/sca-cpp/branches/gcc-4.4/modules/scheme/primitive.hpp new file mode 100644 index 0000000000..fd5f3e9755 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/primitive.hpp @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scheme_primitive_hpp +#define tuscany_scheme_primitive_hpp + +/** + * Script evaluator primitive functions. + */ + +#include <apr_general.h> +#include <apr_uuid.h> +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" + +namespace tuscany { +namespace scheme { + +const value primitiveSymbol("primitive"); +const value quoteSymbol("'"); +const value lambdaSymbol("lambda"); + +#ifdef WANT_THREADS +__thread +#endif +ostream* displayOutStream = NULL; + +#ifdef WANT_THREADS +__thread +#endif +ostream* logOutStream = NULL; + +const bool setupDisplay(ostream& out) { + displayOutStream = &out; + return true; +} + +ostream& displayStream() { + if (displayOutStream == NULL) + return cout; + return *displayOutStream; +} + +const bool setupLog(ostream& out) { + logOutStream = &out; + return true; +} + +ostream& logStream() { + if (logOutStream == NULL) + return cerr; + return *logOutStream; +} + +const value carProc(const list<value>& args) { + return car((list<value> )car(args)); +} + +const value cdrProc(const list<value>& args) { + return cdr((list<value> )car(args)); +} + +const value consProc(const list<value>& args) { + return cons(car(args), (list<value> )cadr(args)); +} + +const value listProc(const list<value>& args) { + return args; +} + +const value assocProc(const list<value>& args) { + return assoc(car(args), (list<list<value> >)cadr(args)); +} + +const value nulProc(const list<value>& args) { + const value v(car(args)); + if (isNil(v)) + return true; + if (isList(v)) + return isNil(list<value>(v)); + return false; +} + +const value equalProc(const list<value>& args) { + return (bool)(car(args) == cadr(args)); +} + +const value addProc(const list<value>& args) { + if (isNil(cdr(args))) + return (double)car(args); + return (double)car(args) + (double)cadr(args); +} + +const value subProc(const list<value>& args) { + if (isNil(cdr(args))) + return (double)0 - (double)car(args); + return (double)car(args) - (double)cadr(args); +} + +const value mulProc(const list<value>& args) { + return (double)car(args) * (double)cadr(args); +} + +const value divProc(const list<value>& args) { + return (double)car(args) / (double)cadr(args); +} + +const value displayProc(const list<value>& args) { + if (isNil(args)) { + displayStream() << endl; + return true; + } + displayStream() << car(args); + return displayProc(cdr(args)); +} + +const value logProc(const list<value>& args) { + if (isNil(args)) { + logStream() << endl; + return true; + } + logStream() << car(args); + return logProc(cdr(args)); +} + +const value uuidProc(unused const list<value>& args) { + apr_uuid_t uuid; + apr_uuid_get(&uuid); + char buf[APR_UUID_FORMATTED_LENGTH]; + apr_uuid_format(buf, &uuid); + return string(buf, APR_UUID_FORMATTED_LENGTH); +} + +const value cadrProc(unused const list<value>& args) { + return cadr((list<value> )car(args)); +} + +const value caddrProc(unused const list<value>& args) { + return caddr((list<value> )car(args)); +} + +const value cadddrProc(unused const list<value>& args) { + return cadddr((list<value> )car(args)); +} + +const value cddrProc(unused const list<value>& args) { + return cddr((list<value> )car(args)); +} + +const value cdddrProc(unused const list<value>& args) { + return cdddr((list<value> )car(args)); +} + +const value startProc(unused const list<value>& args) { + return lambda<value(const list<value>&)>(); +} + +const value stopProc(unused const list<value>& args) { + return lambda<value(const list<value>&)>(); +} + +const value applyPrimitiveProcedure(const value& proc, list<value>& args) { + const lambda<value(const list<value>&)> func(cadr((list<value>)proc)); + return func(args); +} + +const bool isPrimitiveProcedure(const value& proc) { + return isTaggedList(proc, primitiveSymbol); +} + +const bool isSelfEvaluating(const value& exp) { + if(isNil(exp)) + return true; + if(isNumber(exp)) + return true; + if(isString(exp)) + return true; + if(isBool(exp)) + return true; + if(isLambda(exp)) + return true; + return false; +} + +const value primitiveImplementation(const list<value>& proc) { + return car(cdr(proc)); +} + +template<typename F> const value primitiveProcedure(const F& f) { + return mklist<value>(primitiveSymbol, (lambda<value(const list<value>&)>)f); +} + +const list<value> primitiveProcedureNames() { + return mklist<value>("car") + + "cdr" + + "cons" + + "list" + + "nul" + + "=" + + "equal?" + + "+" + + "-" + + "*" + + "/" + + "assoc" + + "cadr" + + "caddr" + + "cadddr" + + "cddr" + + "cdddr" + + "display" + + "log" + + "uuid" + + "start" + + "stop"; +} + +const list<value> primitiveProcedureObjects() { + return mklist(primitiveProcedure(carProc)) + + primitiveProcedure(cdrProc) + + primitiveProcedure(consProc) + + primitiveProcedure(listProc) + + primitiveProcedure(nulProc) + + primitiveProcedure(equalProc) + + primitiveProcedure(equalProc) + + primitiveProcedure(addProc) + + primitiveProcedure(subProc) + + primitiveProcedure(mulProc) + + primitiveProcedure(divProc) + + primitiveProcedure(assocProc) + + primitiveProcedure(cadrProc) + + primitiveProcedure(caddrProc) + + primitiveProcedure(cadddrProc) + + primitiveProcedure(cddrProc) + + primitiveProcedure(cdddrProc) + + primitiveProcedure(displayProc) + + primitiveProcedure(logProc) + + primitiveProcedure(uuidProc) + + primitiveProcedure(startProc) + + primitiveProcedure(stopProc); +} + +const bool isFalse(const value& exp) { + return (bool)exp == false; +} + +const bool isTrue(const value& exp) { + return (bool)exp == true; +} + +const bool isQuoted(const value& exp) { + return isTaggedList(exp, quoteSymbol); +} + +const value textOfQuotation(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value makeLambda(const list<value>& parameters, const list<value>& body) { + return cons(lambdaSymbol, cons<value>(parameters, body)); +} + +} +} +#endif /* tuscany_scheme_primitive_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd b/sca-cpp/branches/gcc-4.4/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd new file mode 100644 index 0000000000..5b32834832 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<schema xmlns="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://tuscany.apache.org/xmlns/sca/1.1" + xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + elementFormDefault="qualified"> + + <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200912" schemaLocation="sca-1.1-cd04.xsd"/> + + <element name="implementation.scheme" type="t:SchemeImplementation" substitutionGroup="sca:implementation"/> + + <complexType name="SchemeImplementation"> + <complexContent> + <extension base="sca:Implementation"> + <sequence> + <any namespace="##targetNamespace" processContents="lax" + minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="script" type="anyURI" use="required"/> + <anyAttribute namespace="##any" processContents="lax"/> + </extension> + </complexContent> + </complexType> + +</schema> diff --git a/sca-cpp/branches/gcc-4.4/modules/server/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/server/Makefile.am new file mode 100644 index 0000000000..06323aad05 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/Makefile.am @@ -0,0 +1,46 @@ +# 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. + +INCLUDES = -I${HTTPD_INCLUDE} + +mod_SCRIPTS = cpp-conf scheme-conf server-conf +moddir = $(prefix)/modules/server + +lib_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la +libdir = $(prefix)/modules/server +noinst_DATA = libmod_tuscany_eval.so libmod_tuscany_wiring.so + +libmod_tuscany_eval_la_SOURCES = mod-eval.cpp +libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_eval.so: + ln -s .libs/libmod_tuscany_eval.so libmod_tuscany_eval.so + +libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp +libmod_tuscany_wiring_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_wiring.so: + ln -s .libs/libmod_tuscany_wiring.so libmod_tuscany_wiring.so + +noinst_testdir = $(prefix)/test +noinst_test_LTLIBRARIES = libimpl-test.la +libimpl_test_la_SOURCES = impl-test.cpp + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +noinst_PROGRAMS = client-test +TESTS = httpd-test server-test wiring-test + diff --git a/sca-cpp/branches/gcc-4.4/modules/server/client-test.cpp b/sca-cpp/branches/gcc-4.4/modules/server/client-test.cpp new file mode 100644 index 0000000000..5de7ab6e7b --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/client-test.cpp @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "client-test.hpp" + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::server::testURI = "http://localhost:8090/test"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/client-test.hpp b/sca-cpp/branches/gcc-4.4/modules/server/client-test.hpp new file mode 100644 index 0000000000..9b37a73803 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/client-test.hpp @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "parallel.hpp" +#include "perf.hpp" +#include "../http/curl.hpp" + +namespace tuscany { +namespace server { + +string testURI = "http://localhost:8090/test"; +bool testBlobs = true; + +ostream* curlWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +const bool testGet() { + gc_scoped_pool pool; + http::CURLSession ch; + { + ostringstream os; + const failable<list<ostream*> > r = http::get<ostream*>(curlWriter, &os, "http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(str(os), "HTTP/1.1 200") || contains(str(os), "HTTP/1.0 200")); + assert(contains(str(os), "It works")); + } + { + const failable<value> r = http::getcontent("http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + } + return true; +} + +struct getLoop { + http::CURLSession ch; + getLoop(http::CURLSession& ch) : ch(ch) { + } + const bool operator()() const { + const failable<value> r = http::getcontent("http://localhost:8090/index.html", ch); + assert(hasContent(r)); + assert(contains(car(reverse(list<value>(content(r)))), "It works")); + return true; + } +}; + +const bool testGetPerf() { + gc_scoped_pool pool; + http::CURLSession ch; + const lambda<bool()> gl = getLoop(ch); + cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl; + return true; +} + +const bool testEval() { + gc_scoped_pool pool; + http::CURLSession ch; + const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), testURI, ch)); + assert(val == string("Hello")); + return true; +} + +struct evalLoop { + const string uri; + http::CURLSession ch; + evalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) { + } + const bool operator()() const { + const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), uri, ch)); + assert(val == string("Hello")); + return true; + } +}; + +const value blob(string(2048, 'A')); +const list<value> blobs = mklist(blob, blob); + +struct blobEvalLoop { + const string uri; + http::CURLSession ch; + blobEvalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) { + } + const bool operator()() const { + const value val = content(http::evalExpr(mklist<value>(string("echo"), blobs), uri, ch)); + assert(val == blobs); + return true; + } +}; + +const bool testEvalPerf() { + gc_scoped_pool pool; + http::CURLSession ch; + const lambda<bool()> el = evalLoop(testURI, ch); + cout << "JSON-RPC eval echo test " << time(el, 5, 200) << " ms" << endl; + + if (testBlobs) { + const lambda<bool()> bel = blobEvalLoop(testURI, ch); + cout << "JSON-RPC eval blob test " << time(bel, 5, 200) << " ms" << endl; + } + return true; +} + +bool testPost() { + gc_scoped_pool pool; + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + http::CURLSession ch; + const failable<value> id = http::post(a, testURI, ch); + assert(hasContent(id)); + return true; +} + +struct postLoop { + const string uri; + const value val; + http::CURLSession ch; + postLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) { + } + const bool operator()() const { + const failable<value> id = http::post(val, uri, ch); + assert(hasContent(id)); + return true; + } +}; + +struct postBlobLoop { + const string uri; + const value val; + http::CURLSession ch; + postBlobLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) { + } + const bool operator()() const { + gc_scoped_pool pool; + const failable<value> id = http::post(val, uri, ch); + assert(hasContent(id)); + return true; + } +}; + +const bool testPostPerf() { + gc_scoped_pool pool; + http::CURLSession ch; + { + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + const lambda<bool()> pl = postLoop(testURI, val, ch); + cout << "ATOMPub POST small test " << time(pl, 5, 200) << " ms" << endl; + } + if (testBlobs) { + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "blob1" + blob) + + (list<value>() + "blob2" + blob) + + (list<value>() + "price" + string("$2.99")); + const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + const lambda<bool()> pl = postBlobLoop(testURI, val, ch); + cout << "ATOMPub POST blob test " << time(pl, 5, 200) << " ms" << endl; + } + return true; +} + +#ifdef WANT_THREADS + +const bool postThread(const string& uri, const int count, const value& val) { + gc_scoped_pool pool; + http::CURLSession ch; + const lambda<bool()> pl = postLoop(uri, val, ch); + time(pl, 0, count); + return true; +} + +const list<future<bool> > startPost(worker& w, const int threads, const lambda<bool()>& l) { + if (threads == 0) + return list<future<bool> >(); + return cons(submit(w, l), startPost(w, threads - 1, l)); +} + +const bool checkPost(const list<future<bool> >& r) { + if (isNil(r)) + return true; + assert(car(r) == true); + return checkPost(cdr(r)); +} + +struct postThreadLoop { + const lambda<bool()> l; + const int threads; + const gc_ptr<worker> w; + postThreadLoop(const lambda<bool()>& l, const int threads) : l(l), threads(threads), w(new (gc_new<worker>()) worker(threads)) { + } + const bool operator()() const { + list<future<bool> > r = startPost(*w, threads, l); + checkPost(r); + return true; + } +}; + +const bool testPostThreadPerf() { + gc_scoped_pool pool; + const int count = 50; + const int threads = 10; + + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postThread), testURI, count, val); + const lambda<bool()> ptl = postThreadLoop(pl, threads); + double t = time(ptl, 0, 1) / (threads * count); + cout << "ATOMPub POST thread test " << t << " ms" << endl; + + return true; +} + +#else + +const bool postProc(const string& uri, const int count, const value& val) { + gc_scoped_pool pool; + http::CURLSession ch; + const lambda<bool()> pl = postLoop(uri, val, ch); + time(pl, 0, count); + return true; +} + +const list<pid_t> startPost(const int procs, const lambda<bool()>& l) { + if (procs == 0) + return list<pid_t>(); + pid_t pid = fork(); + if (pid == 0) { + assert(l() == true); + exit(0); + } + return cons(pid, startPost(procs - 1, l)); +} + +const bool checkPost(const list<pid_t>& r) { + if (isNil(r)) + return true; + int status; + waitpid(car(r), &status, 0); + assert(status == 0); + return checkPost(cdr(r)); +} + +struct postForkLoop { + const lambda<bool()> l; + const int procs; + postForkLoop(const lambda<bool()>& l, const int procs) : l(l), procs(procs) { + } + const bool operator()() const { + list<pid_t> r = startPost(procs, l); + checkPost(r); + return true; + } +}; + +const bool testPostForkPerf() { + gc_scoped_pool pool; + const int count = 50; + const int procs = 10; + + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postProc), testURI, count, val); + const lambda<bool()> ptl = postForkLoop(pl, procs); + double t = time(ptl, 0, 1) / (procs * count); + cout << "ATOMPub POST fork test " << t << " ms" << endl; + + return true; +} + +#endif + +const bool testPut() { + gc_scoped_pool pool; + const list<value> i = list<value>() + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99")); + const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + http::CURLSession ch; + value rc = content(http::put(a, testURI + "/111", ch)); + assert(rc == value(true)); + return true; +} + +const bool testDel() { + gc_scoped_pool pool; + http::CURLSession ch; + value rc = content(http::del(testURI + "/111", ch)); + assert(rc == value(true)); + return true; +} + +const bool testServer() { + tuscany::server::testGet(); + tuscany::server::testPost(); + tuscany::server::testPut(); + tuscany::server::testDel(); + tuscany::server::testEval(); + tuscany::server::testGetPerf(); + tuscany::server::testPostPerf(); +#ifdef WANT_THREADS + tuscany::server::testPostThreadPerf(); +#else + tuscany::server::testPostForkPerf(); +#endif + tuscany::server::testEvalPerf(); + return true; +} + +} +} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/client-test.scm b/sca-cpp/branches/gcc-4.4/modules/server/client-test.scm new file mode 100644 index 0000000000..47b799d390 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/client-test.scm @@ -0,0 +1,38 @@ +; 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. + +; JSON-RPC test case + +(define (echo x ref) (ref "echo" x)) + +; ATOMPub test case + +(define (get id ref) + (ref "get" id) +) + +(define (post coll entry ref) + (ref "post" coll entry) +) + +(define (put id entry ref) + (ref "put" id entry) +) + +(define (delete id ref) + (ref "delete" id) +) diff --git a/sca-cpp/branches/gcc-4.4/modules/server/cpp-conf b/sca-cpp/branches/gcc-4.4/modules/server/cpp-conf new file mode 100755 index 0000000000..bc014ac979 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/cpp-conf @@ -0,0 +1,28 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a C++ server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +cat >>$root/conf/httpd.conf <<EOF +# Support for C++ SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so + +EOF diff --git a/sca-cpp/branches/gcc-4.4/modules/server/domain-test.composite b/sca-cpp/branches/gcc-4.4/modules/server/domain-test.composite new file mode 100644 index 0000000000..ef85bb919b --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/domain-test.composite @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="scheme-test"> + <t:implementation.scheme script="server-test.scm"/> + <service name="test"> + <t:binding.http uri="test"/> + </service> + </component> + + <component name="cpp-test"> + <implementation.cpp path=".libs" library="libimpl-test"/> + <service name="cpp"> + <t:binding.http uri="cpp"/> + </service> + </component> + + <component name="client-test"> + <t:implementation.scheme script="client-test.scm"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="scheme-test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/gcc-4.4/modules/server/htdocs/entry.xml b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/entry.xml new file mode 100644 index 0000000000..86b8a10547 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/entry.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry> diff --git a/sca-cpp/branches/gcc-4.4/modules/server/htdocs/feed.xml b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/feed.xml new file mode 100644 index 0000000000..5e37de6580 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/feed.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333"/></entry></feed> diff --git a/sca-cpp/branches/gcc-4.4/modules/server/htdocs/index.html b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/index.html new file mode 100644 index 0000000000..1bfb3e30c2 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/index.html @@ -0,0 +1,21 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + +<html><body><h1>It works!</h1></body></html> + diff --git a/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-request.txt b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-request.txt new file mode 100644 index 0000000000..b4bd07fc46 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-request.txt @@ -0,0 +1 @@ +{"id":1,"method":"echo","params":["Hello"]} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-result.txt b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-result.txt new file mode 100644 index 0000000000..121bf74902 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/htdocs/json-result.txt @@ -0,0 +1 @@ +{"id":1,"result":"Hello"}
\ No newline at end of file diff --git a/sca-cpp/branches/gcc-4.4/modules/server/httpd-test b/sca-cpp/branches/gcc-4.4/modules/server/httpd-test new file mode 100755 index 0000000000..13347ea83f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/httpd-test @@ -0,0 +1,78 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/server/impl-test.cpp b/sca-cpp/branches/gcc-4.4/modules/server/impl-test.cpp new file mode 100644 index 0000000000..1bd0843745 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/impl-test.cpp @@ -0,0 +1,76 @@ +/* + * 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 component implementation. + */ + +#include "string.hpp" + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace server { + +const failable<value> get(unused const list<value>& params) { + return value(mklist<value>("text/html", mklist<value>("Hey"))); +} + +const failable<value> post(unused const list<value>& params) { + return value(mklist<value>(string("123456789"))); +} + +const failable<value> put(unused const list<value>& params) { + return value(true); +} + +const failable<value> del(unused const list<value>& params) { + return value(true); +} + +const failable<value> echo(const list<value>& params) { + return value(car(params)); +} + +} +} + +extern "C" { + +const tuscany::value apply(const tuscany::list<tuscany::value>& params) { + const tuscany::value func(car(params)); + if (func == "get") + return tuscany::server::get(cdr(params)); + if (func == "post") + return tuscany::server::post(cdr(params)); + if (func == "put") + return tuscany::server::put(cdr(params)); + if (func == "delete") + return tuscany::server::del(cdr(params)); + if (func == "echo") + return tuscany::server::echo(cdr(params)); + return tuscany::mkfailure<tuscany::value>(); +} + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/mod-cpp.hpp b/sca-cpp/branches/gcc-4.4/modules/server/mod-cpp.hpp new file mode 100644 index 0000000000..b3828dd7d2 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/mod-cpp.hpp @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modcpp_hpp +#define tuscany_modcpp_hpp + +/** + * Evaluation functions used by mod-eval to evaluate C++ + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" + +#include "function.hpp" +#include "list.hpp" +#include "element.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "dynlib.hpp" +#include "../scheme/driver.hpp" + +namespace tuscany { +namespace server { +namespace modcpp { + +/** + * Apply a C++ component implementation function. + */ +const list<value> failableResult(const value& func, const list<value>& v) { + if (isNil(cdr(v))) + return v; + + // Report a failure with an empty reason as 'function not supported' + // Except for the start, and stop functions, which are optional + const value reason = cadr(v); + if (length(reason) == 0) { + if (func == "start" || func == "stop") + return mklist<value>(lambda<value(const list<value>&)>()); + return mklist<value>(value(), string("Function not supported: ") + func); + } + return v; +} + +struct applyImplementation { + const lib ilib; + const lambda<value(const list<value>&)> impl; + const list<value> px; + applyImplementation(const lib& ilib, const lambda<value(const list<value>&)>& impl, const list<value>& px) : ilib(ilib), impl(impl), px(px) { + } + const value operator()(const list<value>& params) const { + debug(params, "modeval::cpp::applyImplementation::input"); + + // Apply the component implementation function + const value val = failableResult(car(params), impl(append(params, px))); + + debug(val, "modeval::cpp::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a C++ component implementation and convert it to + * an applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) { + + // Configure the implementation's lambda function + const value ipath(attributeValue("path", impl)); + const value iname(attributeValue("library", impl)); + const string fpath(isNil(ipath)? path + iname : path + ipath + "/" + iname); + const lib ilib(*(new (gc_new<lib>()) lib(fpath + dynlibExt))); + const failable<lambda<value(const list<value>&)> > evalf(dynlambda<value(const list<value>&)>("apply", ilib)); + if (!hasContent(evalf)) + return evalf; + const lambda<value(const list<value>&)> l(applyImplementation(ilib, content(evalf), px)); + return l; +} + +} +} +} + +#endif /* tuscany_modcpp_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.cpp b/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.cpp new file mode 100644 index 0000000000..a94ccf5bbe --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.cpp @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval C++ and Scheme component implementations. + */ + +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "mod-eval.hpp" +#include "mod-scheme.hpp" +#include "mod-cpp.hpp" + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Apply a lifecycle start or restart event. + */ +const value applyLifecycle(unused const list<value>& params) { + // Return a nil function as we don't need to handle any subsequent events + return failable<value>(lambda<value(const list<value>&)>()); +} + +/** + * Evaluate a Scheme or C++ component implementation and convert it to an + * applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) { + const string itype(elementName(impl)); + if (contains(itype, ".scheme")) + return modscheme::evalImplementation(path, impl, px); + if (contains(itype, ".cpp")) + return modcpp::evalImplementation(path, impl, px); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.hpp b/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.hpp new file mode 100644 index 0000000000..5ce787d2fb --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/mod-eval.hpp @@ -0,0 +1,615 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modeval_hpp +#define tuscany_modeval_hpp + +/** + * HTTPD module used to eval component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../scdl/scdl.hpp" +#include "../http/curl.hpp" +#include "../http/httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(server_rec* s) : s(s), wiringServerName(""), contributionPath(""), compositeName(""), ca(""), cert(""), key("") { + } + + const server_rec* s; + lambda<value(const list<value>&)> lifecycle; + string wiringServerName; + string contributionPath; + string compositeName; + string ca; + string cert; + string key; + list<value> implementations; + list<value> implTree; +}; + +/** + * Convert a result represented as a content + failure pair to a + * failable monad. + */ +const failable<value> failableResult(const list<value>& v) { + if (isNil(cdr(v))) + return car(v); + return mkfailure<value>(string(cadr(v))); +} + +/** + * Handle an HTTP GET. + */ +const failable<int> get(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::get::uri"); + + // Inspect the query string + const list<list<value> > args = httpd::queryArgs(r); + const list<value> ia = assoc(value("id"), args); + const list<value> ma = assoc(value("method"), args); + + // Evaluate a JSON-RPC request and return a JSON result + if (!isNil(ia) && !isNil(ma)) { + + // Extract the request id, method and params + const value id = cadr(ia); + const value func = c_str(json::funcName(string(cadr(ma)))); + + // Apply the requested function + const failable<value> val = failableResult(impl(cons(func, httpd::queryParams(args)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return JSON result + json::JSONContext cx; + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate the GET expression + const list<value> path(pathValues(r->uri)); + const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(cddr(path))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + const value c = content(val); + + // Write returned content-type / content-list pair + if (isList(cadr<value>(c))) + return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r); + + // Write returned ATOM feed or entry + if (isNil(cddr(path))) + return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(c)), "application/atom+xml;type=feed", r); + else + return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(c)), "application/atom+xml;type=entry", r); +} + +/** + * Handle an HTTP POST. + */ +const failable<int> post(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::post::url"); + + // Evaluate a JSON-RPC request and return a JSON result + const string ct = httpd::contentType(r); + if (contains(ct, "application/json-rpc") || contains(ct, "text/plain")) { + + // Read the JSON request + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::post::input"); + json::JSONContext cx; + const list<value> json = elementsToValues(content(json::readJSON(ls, cx))); + const list<list<value> > args = httpd::postArgs(json); + + // Extract the request id, method and params + const value id = cadr(assoc(value("id"), args)); + const value func = c_str(json::funcName(cadr(assoc(value("method"), args)))); + const list<value> params = (list<value>)cadr(assoc(value("params"), args)); + + // Evaluate the request expression + const failable<value> val = failableResult(impl(cons<value>(func, params))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return JSON result + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate an ATOM POST request and return the location of the corresponding created resource + if (contains(ct, "application/atom+xml")) { + + // Read the ATOM entry + const list<value> path(pathValues(r->uri)); + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::post::input"); + const value entry = atom::entryValue(content(atom::readATOMEntry(ls))); + + // Evaluate the POST expression + const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(path), entry)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + + // Return the created resource location + debug(content(val), "modeval::post::location"); + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, httpd::url(content(val), r))); + r->status = HTTP_CREATED; + return OK; + } + + // Unknown content type, wrap the HTTP request struct in a value and pass it to + // the component implementation function + const failable<value> val = failableResult(impl(cons<value>("handle", mklist<value>(httpd::requestValue(r))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + return (int)content(val); +} + +/** + * Handle an HTTP PUT. + */ +const failable<int> put(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::put::url"); + + // Read the ATOM entry + const list<value> path(pathValues(r->uri)); + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::put::input"); + const value entry = atom::entryValue(content(atom::readATOMEntry(ls))); + + // Evaluate the PUT expression and update the corresponding resource + const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(path), entry)))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable<int> del(request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::delete::url"); + + // Evaluate an ATOM delete request + const list<value> path(pathValues(r->uri)); + const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(path))))); + if (!hasContent(val)) + return mkfailure<int>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Translate a component request. + */ +int translate(request_rec *r) { + gc_scoped_pool pool(r->pool); + if (strncmp(r->uri, "/components/", 12) != 0) + return DECLINED; + r->handler = "mod_tuscany_eval"; + return OK; +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + gc_scoped_pool pool(r->pool); + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + httpdDebugRequest(r, "modeval::handler::input"); + + // Get the component implementation lambda + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + const list<value> path(pathValues(r->uri)); + const list<value> impl(assoctree<value>(cadr(path), sc.implTree)); + if (isNil(impl)) + return httpd::reportStatus(mkfailure<int>(string("Couldn't find component implementation"))); + + // Handle HTTP method + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return httpd::reportStatus(get(r, l)); + if(r->method_number == M_POST) + return httpd::reportStatus(post(r, l)); + if(r->method_number == M_PUT) + return httpd::reportStatus(put(r, l)); + if(r->method_number == M_DELETE) + return httpd::reportStatus(del(r, l)); + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Convert a list of component references to a list of HTTP proxy lambdas. + */ +const value mkrefProxy(const value& ref, const string& base, const string& ca, const string& cert, const string& key) { + return lambda<value(const list<value>&)>(http::proxy(base + string(scdl::name(ref)), ca, cert, key)); +} + +const list<value> refProxies(const list<value>& refs, const string& base, const string& ca, const string& cert, const string& key) { + if (isNil(refs)) + return refs; + return cons(mkrefProxy(car(refs), base, ca, cert, key), refProxies(cdr(refs), base, ca, cert, key)); +} + +/** + * 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(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 lambda<value(const list<value>&)>& lifecycle); + + const value impl = scdl::implementation(comp); + + // Convert component references to configured proxy lambdas + ostringstream base; + if (sc.wiringServerName == "") + base << (server.server_scheme == NULL? "http" : server.server_scheme) + << "://" << (server.server_hostname == NULL? "localhost" : server.server_hostname) + << ":" << (server.port == 0? 80 : server.port) + << "/references/" << string(scdl::name(comp)) << "/"; + else + base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; + const list<value> rpx(refProxies(scdl::references(comp), str(base), sc.ca, sc.cert, sc.key)); + + // 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(sc.contributionPath, impl, append(rpx, ppx), sc.lifecycle)); + if (!hasContent(cimpl)) + return reason(cimpl); + return content(cimpl); +} + +/** + * Return a list of component-name + configured-implementation pairs. + */ +const list<value> componentToImplementationAssoc(ServerConf& sc, server_rec& server, const list<value>& c) { + if (isNil(c)) + return c; + return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, server, car(c))), componentToImplementationAssoc(sc, server, cdr(c))); +} + +/** + * Read the components declared in a composite. + */ +const failable<list<value> > readComponents(const string& path) { + ifstream is(path); + if (fail(is)) + return mkfailure<list<value> >(string("Could not read composite: ") + path); + return scdl::components(readXML(streamList(is))); +} + +/** + * Apply a list of component implementations to a start or restart lifecycle expression. + * Return the functions returned by the component implementations. + */ +const failable<list<value> > applyLifecycleExpr(const list<value>& impls, const list<value>& expr) { + if (isNil(impls)) + return list<value>(); + + // Evaluate lifecycle expression against a component implementation lambda + const lambda<value(const list<value>&)> l = cadr<value>(car(impls)); + const failable<value> r = failableResult(l(expr)); + if (!hasContent(r)) + return mkfailure<list<value> >(reason(r)); + const lambda<value(const list<value>&)> rl = content(r); + + // Use the returned lambda function, if any, from now on + const lambda<value(const list<value>&)> al = isNil(rl)? l : rl; + + // Continue with the rest of the list + const failable<list<value> > nr = applyLifecycleExpr(cdr(impls), expr); + if (!hasContent(nr)) + return nr; + return cons<value>(mklist<value>(car<value>(car(impls)), value(al)), content(nr)); +} + +/** + * Configure the components declared in the deployed composite. + */ +const failable<bool> confComponents(ServerConf& sc, server_rec& server) { + if (sc.contributionPath == "" || sc.compositeName == "") + return false; + + // Read the components and get their implementation lambda functions + const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName); + if (!hasContent(comps)) + return mkfailure<bool>(reason(comps)); + sc.implementations = componentToImplementationAssoc(sc, server, content(comps)); + debug(sc.implementations, "modeval::confComponents::implementations"); + + // Store the implementation lambda functions in a tree for fast retrieval + sc.implTree = mkbtree(sort(sc.implementations)); + return true; +} + +/** + * Start the components declared in the deployed composite. + */ +const failable<bool> startComponents(ServerConf& sc) { + + // Start the components and record the returned implementation lambda functions + debug(sc.implementations, "modeval::startComponents::start"); + const failable<list<value> > impls = applyLifecycleExpr(sc.implementations, mklist<value>("start")); + if (!hasContent(impls)) + return mkfailure<bool>(reason(impls)); + sc.implementations = content(impls); + debug(sc.implementations, "modeval::startComponents::implementations"); + + // Store the implementation lambda functions in a tree for fast retrieval + sc.implTree = mkbtree(sort(sc.implementations)); + return true; +} + +/** + * Cleanup callback, called when the server is stopped or restarted. + */ +apr_status_t serverCleanup(void* v) { + gc_pool pool; + ServerConf& sc = *(ServerConf*)v; + debug("modeval::serverCleanup"); + + // Stop the component implementations + applyLifecycleExpr(sc.implementations, mklist<value>("stop")); + + // Call the module lifecycle function + if (isNil(sc.lifecycle)) + return APR_SUCCESS; + debug("modeval::serverCleanup::stop"); + sc.lifecycle(mklist<value>("stop")); + + return APR_SUCCESS; +} + +/** + * Called after all the configuration commands have been run. + * Process the server configuration and configure the deployed components. + */ +const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + sc.wiringServerName = mainsc.wiringServerName; + sc.contributionPath = mainsc.contributionPath; + sc.compositeName = mainsc.compositeName; + sc.ca = mainsc.ca; + sc.cert = mainsc.cert; + sc.key = mainsc.key; + sc.implementations = mainsc.implementations; + sc.implTree = mainsc.implTree; + return postConfigMerge(mainsc, s->next); +} + +int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) { + extern const value applyLifecycle(const list<value>&); + + gc_scoped_pool pool(p); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + + // Count the calls to post config + const string k("tuscany::modeval::postConfig"); + const int count = (int)httpd::userData(k, s); + httpd::putUserData(k, (void*)(count + 1), s); + + // Count == 0, do nothing as post config is always called twice, + // count == 1 is the first start, count > 1 is a restart + if (count == 0) + return OK; + if (count == 1) { + debug("modeval::postConfig::start"); + const failable<value> r = failableResult(applyLifecycle(mklist<value>("start"))); + if (!hasContent(r)) + return -1; + sc.lifecycle = content(r); + } + if (count > 1) { + debug("modeval::postConfig::restart"); + const failable<value> r = failableResult(applyLifecycle(mklist<value>("restart"))); + if (!hasContent(r)) + return -1; + sc.lifecycle = content(r); + } + + // Configure the deployed components + debug(sc.wiringServerName, "modeval::postConfig::wiringServerName"); + debug(sc.contributionPath, "modeval::postConfig::contributionPath"); + debug(sc.compositeName, "modeval::postConfig::compositeName"); + const failable<bool> res = confComponents(sc, *s); + if (!hasContent(res)) { + cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + return -1; + } + + // Register a cleanup callback, called when the server is stopped or restarted + apr_pool_pre_cleanup_register(p, (void*)&sc, serverCleanup); + + // Merge the config into any virtual hosts + return postConfigMerge(sc, s->next); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* s) { + gc_scoped_pool pool(p); + ServerConf* sc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval); + if(sc == NULL) { + cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + + // Start the components in the child process + const failable<bool> res = startComponents(*sc); + if (!hasContent(res)) { + cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + + // Register a cleanup callback, called when the child is stopped or restarted + apr_pool_pre_cleanup_register(p, (void*)sc, serverCleanup); +} + +/** + * Configuration commands. + */ +const char* confWiringServerName(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.wiringServerName = arg; + return NULL; +} +const char* confContribution(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.contributionPath = arg; + return NULL; +} +const char* confComposite(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.compositeName = arg; + return NULL; +} +const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.ca = arg; + return NULL; +} +const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.cert = arg; + return NULL; +} +const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.key = arg; + return NULL; +} + +const char* confEnv(unused cmd_parms *cmd, unused void *c, const char *name, const char *value) { + gc_scoped_pool pool(cmd->pool); + setenv(name, value != NULL? value : "", 1); + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"), + AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"), + AP_INIT_TAKE1("SCASSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SSL CA certificate file"), + AP_INIT_TAKE1("SCASSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SSL certificate file"), + AP_INIT_TAKE1("SCASSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "SSL certificate key file"), + {NULL, NULL, NULL, 0, NO_ARGS, NULL} +}; + + +void registerHooks(unused apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_eval = { + STANDARD20_MODULE_STUFF, + // dir config and merger + NULL, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::server::modeval::ServerConf>, NULL, + // commands and hooks + tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks +}; + +} + +#endif diff --git a/sca-cpp/branches/gcc-4.4/modules/server/mod-scheme.hpp b/sca-cpp/branches/gcc-4.4/modules/server/mod-scheme.hpp new file mode 100644 index 0000000000..f5b9554c3f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/mod-scheme.hpp @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_modscheme_hpp +#define tuscany_modscheme_hpp + +/** + * Evaluation functions used by mod-eval to evaluate Scheme + * component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../scheme/eval.hpp" + +namespace tuscany { +namespace server { +namespace modscheme { + +/** + * Convert proxy lambdas to evaluator primitive procedures. + */ +const list<value> primitiveProcedures(const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(mklist<value>(scheme::primitiveSymbol, car(l)), primitiveProcedures(cdr(l))); +} + +/** + * Apply a Scheme component implementation function. + */ +struct applyImplementation { + const value impl; + const list<value> px; + applyImplementation(const value& impl, const list<value>& px) : impl(impl), px(scheme::quotedParameters(primitiveProcedures(px))) { + } + const value operator()(const list<value>& params) const { + const value expr = cons<value>(car(params), append(scheme::quotedParameters(cdr(params)), px)); + debug(expr, "modeval::scheme::applyImplementation::input"); + scheme::Env env = scheme::setupEnvironment(); + const value res = scheme::evalScript(expr, impl, env); + const value val = isNil(res)? mklist<value>(value(), string("Could not evaluate expression")) : mklist<value>(res); + debug(val, "modeval::scheme::applyImplementation::result"); + return val; + } +}; + +/** + * Evaluate a Scheme component implementation and convert it to an + * applicable lambda function. + */ +const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) { + const string fpath(path + attributeValue("script", impl)); + ifstream is(fpath); + if (fail(is)) + return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath); + const value script = scheme::readScript(is); + if (isNil(script)) + return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath); + return lambda<value(const list<value>&)>(applyImplementation(script, px)); +} + +} +} +} + +#endif /* tuscany_modscheme_hpp */ diff --git a/sca-cpp/branches/gcc-4.4/modules/server/mod-wiring.cpp b/sca-cpp/branches/gcc-4.4/modules/server/mod-wiring.cpp new file mode 100644 index 0000000000..296181acfa --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/mod-wiring.cpp @@ -0,0 +1,379 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to wire component references and route requests to + * target service components. + */ + +#include <sys/stat.h> + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../scdl/scdl.hpp" +#include "../http/httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring; +} + +namespace tuscany { +namespace server { +namespace modwiring { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(server_rec* s) : s(s), contributionPath(""), compositeName("") { + } + const server_rec* s; + string contributionPath; + string compositeName; + list<value> references; + list<value> services; +}; + +/** + * Set to true to wire using mod_proxy, false to wire using HTTP client redirects. + */ +const bool useModProxy = true; + +/** + * Returns true if a URI is absolute. + */ +const bool isAbsolute(const string& uri) { + return contains(uri, "://"); +} + +/** + * Route a /references/component-name/reference-name request, + * to the target of the component reference. + */ +int translateReference(request_rec *r) { + httpdDebugRequest(r, "modwiring::translateReference::input"); + debug(r->uri, "modwiring::translateReference::uri"); + + // Find the requested component + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring); + const list<value> rpath(pathValues(r->uri)); + const list<value> comp(assoctree(cadr(rpath), sc.references)); + if (isNil(comp)) + return HTTP_NOT_FOUND; + + // Find the requested reference and target configuration + const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp))); + if (isNil(ref)) + return HTTP_NOT_FOUND; + const string target(cadr(ref)); + debug(target, "modwiring::translateReference::target"); + + // Route to an absolute target URI using mod_proxy or an HTTP client redirect + if (isAbsolute(target)) { + if (useModProxy) { + r->filename = apr_pstrdup(r->pool, c_str(string("proxy:") + target)); + r->proxyreq = PROXYREQ_REVERSE; + r->handler = "proxy-server"; + return OK; + } + + r->status = HTTP_MOVED_TEMPORARILY; + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(target))); + r->handler = "mod_tuscany_wiring"; + return OK; + } + + // Route to a relative target URI using a local internal redirect + r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/components/") + target)); + r->handler = "mod_tuscany_wiring"; + return OK; +} + +/** + * Find a leaf matching a request path in a tree of URI paths. + */ +const int matchPath(const list<value>& k, const list<value>& p) { + if (isNil(p)) + return true; + if (isNil(k)) + return false; + if (car(k) != car(p)) + return false; + return matchPath(cdr(k), cdr(p)); +} + +const list<value> assocPath(const value& k, const list<value>& tree) { + if (isNil(tree)) + return tree; + if (matchPath(k, car<value>(car(tree)))) + return car(tree); + if (k < car<value>(car(tree))) + return assocPath(k, cadr(tree)); + return assocPath(k, caddr(tree)); +} + +/** + * Route a service request to the component providing the requested service. + */ +int translateService(request_rec *r) { + httpdDebugRequest(r, "modwiring::translateService::input"); + debug(r->uri, "modwiring::translateService::uri"); + + // Find the requested component + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring); + debug(sc.services, "modwiring::translateService::services"); + const list<value> p(pathValues(r->uri)); + const list<value> svc(assocPath(p, sc.services)); + if (isNil(svc)) + return DECLINED; + debug(svc, "modwiring::translateService::service"); + + // Build a component-name + path-info URI + const list<value> target(cons<value>(cadr(svc), httpd::pathInfo(p, car(svc)))); + debug(target, "modwiring::translateService::target"); + + // Dispatch to the target component using a local internal redirect + const string tp(path(target)); + debug(tp, "modwiring::translateService::path"); + const string redir(string("/redirect:/components") + tp); + debug(redir, "modwiring::translateService::redirect"); + r->filename = apr_pstrdup(r->pool, c_str(redir)); + r->handler = "mod_tuscany_wiring"; + return OK; +} + +/** + * Translate an HTTP service or reference request and route it + * to the target component. + */ +int translate(request_rec *r) { + gc_scoped_pool pool(r->pool); + if (!strncmp(r->uri, "/components/", 12) != 0) + return DECLINED; + + // Translate a component reference request + if (!strncmp(r->uri, "/references/", 12) != 0) + return translateReference(r); + + // Translate a service request + return translateService(r); +} + +/** + * HTTP request handler, redirect to a target component. + */ +int handler(request_rec *r) { + gc_scoped_pool pool(r->pool); + if(strcmp(r->handler, "mod_tuscany_wiring")) + return DECLINED; + httpdDebugRequest(r, "modwiring::handler::input"); + + // Do an internal redirect + if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) + return DECLINED; + debug(r->uri, "modwiring::handler::uri"); + debug(r->filename, "modwiring::handler::filename"); + debug(r->path_info, "modwiring::handler::path info"); + + if (r->args == NULL) + return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r); + return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r); +} + +/** + * Read the components declared in a composite. + */ +const failable<list<value> > readComponents(const string& path) { + ifstream is(path); + if (fail(is)) + return mkfailure<list<value> >(string("Could not read composite: ") + path); + return scdl::components(readXML(streamList(is))); +} + +/** + * Return a list of component-name + references pairs. The references are + * arranged in trees of reference-name + reference-target pairs. + */ +const list<value> componentReferenceToTargetTree(const value& c) { + return mklist<value>(scdl::name(c), mkbtree(sort(scdl::referenceToTargetAssoc(scdl::references(c))))); +} + +const list<value> componentReferenceToTargetAssoc(const list<value>& c) { + if (isNil(c)) + return c; + return cons<value>(componentReferenceToTargetTree(car(c)), componentReferenceToTargetAssoc(cdr(c))); +} + +/** + * Return a list of service-URI-path + component-name pairs. Service-URI-paths are + * represented as lists of URI path fragments. + */ +const list<value> defaultBindingURI(const string& cn, const string& sn) { + return mklist<value>(cn, sn); +} + +const list<value> bindingToComponentAssoc(const string& cn, const string& sn, const list<value>& b) { + if (isNil(b)) + return b; + const value uri(scdl::uri(car(b))); + if (isNil(uri)) + return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), bindingToComponentAssoc(cn, sn, cdr(b))); + return cons<value>(mklist<value>(pathValues(c_str(string(uri))), cn), bindingToComponentAssoc(cn, sn, cdr(b))); +} + +const list<value> serviceToComponentAssoc(const string& cn, const list<value>& s) { + if (isNil(s)) + return s; + const string sn(scdl::name(car(s))); + const list<value> btoc(bindingToComponentAssoc(cn, sn, scdl::bindings(car(s)))); + if (isNil(btoc)) + return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), serviceToComponentAssoc(cn, cdr(s))); + return append<value>(btoc, serviceToComponentAssoc(cn, cdr(s))); +} + +const list<value> uriToComponentAssoc(const list<value>& c) { + if (isNil(c)) + return c; + return append<value>(serviceToComponentAssoc(scdl::name(car(c)), scdl::services(car(c))), uriToComponentAssoc(cdr(c))); +} + +/** + * Configure the components declared in the server's deployment composite. + */ +const bool confComponents(ServerConf& sc) { + if (sc.contributionPath == "" || sc.compositeName == "") + return true; + + // Read the component configuration and store the references and service URIs + // in trees for fast retrieval later + const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName); + if (!hasContent(comps)) + return true; + const list<value> refs = componentReferenceToTargetAssoc(content(comps)); + debug(refs, "modwiring::confComponents::references"); + sc.references = mkbtree(sort(refs)); + + const list<value> svcs = uriToComponentAssoc(content(comps)); + debug(svcs, "modwiring::confComponents::services"); + sc.services = mkbtree(sort(svcs)); + return true; +} + +/** + * Called after all the configuration commands have been run. + * Process the server configuration and configure the wiring for the deployed components. + */ +const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring); + sc.contributionPath = mainsc.contributionPath; + sc.compositeName = mainsc.compositeName; + sc.references = mainsc.references; + sc.services = mainsc.services; + return postConfigMerge(mainsc, s->next); +} + +int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) { + // Count the calls to post config, skip the first one as + // postConfig is always called twice + const string k("tuscany::modwiring::postConfig"); + const int count = (int)httpd::userData(k, s); + httpd::putUserData(k, (void*)(count + 1), s); + if (count == 0) + return OK; + + // Configure the wiring for the deployed components + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring); + debug(sc.contributionPath, "modwiring::postConfig::contributionPath"); + debug(sc.compositeName, "modwiring::postConfig::compositeName"); + confComponents(sc); + + // Merge the config into any virtual hosts + return postConfigMerge(sc, s->next); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* svr_rec) { + gc_scoped_pool pool(p); + ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring); + if(conf == NULL) { + cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } +} + +/** + * Configuration commands. + */ +const char *confContribution(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring); + sc.contributionPath = arg; + return NULL; +} +const char *confComposite(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring); + sc.compositeName = arg; + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"), + {NULL, NULL, NULL, 0, NO_ARGS, NULL} +}; + +void registerHooks(unused apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_wiring = { + STANDARD20_MODULE_STUFF, + // dir config and merger + NULL, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::server::modwiring::ServerConf>, NULL, + // commands and hooks + tuscany::server::modwiring::commands, tuscany::server::modwiring::registerHooks +}; + +} diff --git a/sca-cpp/branches/gcc-4.4/modules/server/scheme-conf b/sca-cpp/branches/gcc-4.4/modules/server/scheme-conf new file mode 100755 index 0000000000..fc5f2b3ac8 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/scheme-conf @@ -0,0 +1,28 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a Scheme server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +cat >>$root/conf/httpd.conf <<EOF +# Support for Scheme SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so + +EOF diff --git a/sca-cpp/branches/gcc-4.4/modules/server/server-conf b/sca-cpp/branches/gcc-4.4/modules/server/server-conf new file mode 100755 index 0000000000..ed5ead4cf5 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/server-conf @@ -0,0 +1,47 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a server conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` + +host=`cat $root/conf/httpd.conf | grep ServerName | awk '{ print $2 }'` +port=`cat $root/conf/httpd.conf | grep Listen | tail -1 | awk '{ print $2 }'` +ssl=`cat $root/conf/httpd.conf | grep "SSLEngine" | awk '{ print $2 }'` +if [ "$ssl" = "on" ]; then + protocol="https" + +cat >>$root/conf/httpd.conf <<EOF +# Configure SCA SSL support +SCASSLCACertificateFile "$root/conf/ca.crt" +SCASSLCertificateFile "$root/conf/server.crt" +SCASSLCertificateKeyFile "$root/conf/server.key" + +EOF + +else + protocol="http" +fi + +cat >>$root/conf/httpd.conf <<EOF +# Support for SCA component wiring +LoadModule mod_tuscany_wiring $here/libmod_tuscany_wiring.so +SCAWiringServerName $protocol://$host:$port + +EOF diff --git a/sca-cpp/branches/gcc-4.4/modules/server/server-test b/sca-cpp/branches/gcc-4.4/modules/server/server-test new file mode 100755 index 0000000000..e53c7f5ef1 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/server-test @@ -0,0 +1,39 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +../http/httpd-stop tmp +sleep 2 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/server/server-test.scm b/sca-cpp/branches/gcc-4.4/modules/server/server-test.scm new file mode 100644 index 0000000000..5d545ecf8b --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/server-test.scm @@ -0,0 +1,44 @@ +; 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. + +; JSON-RPC test case + +(define (echo x) x) + +; ATOMPub test case + +(define (get id) + (if (nul id) + '("Sample Feed" "123456789" + ("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + ("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))) + ("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55)))) + + (list "Item" (car id) '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))) +) + +(define (post collection item) + '("123456789") +) + +(define (put id item) + true +) + +(define (delete id) + true +) diff --git a/sca-cpp/branches/gcc-4.4/modules/server/wiring-test b/sca-cpp/branches/gcc-4.4/modules/server/wiring-test new file mode 100755 index 0000000000..f821d15256 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/server/wiring-test @@ -0,0 +1,78 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +../http/httpd-conf tmp localhost 8090 htdocs +./server-conf tmp +./scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite domain-test.composite +EOF + +../http/httpd-start tmp +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/Makefile.am b/sca-cpp/branches/gcc-4.4/modules/wsgi/Makefile.am new file mode 100644 index 0000000000..7678104761 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/Makefile.am @@ -0,0 +1,52 @@ +# 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. + +if WANT_PYTHON + +INCLUDES = -I${PYTHON_INCLUDE} + +mod_SCRIPTS = composite.py scdl.py util.py elemutil.py xmlutil.py atomutil.py jsonutil.py wsgi-start wsgi-stop +moddir = $(prefix)/modules/wsgi + +noinst_DATA = target.stamp + +target.stamp: app.yaml *.py *.composite htdocs/* + mkdir -p target + cp app.yaml *.py *.composite target + cp -R htdocs target/htdocs + touch target.stamp + +clean-local: + rm -rf target.stamp target + +prefix_DATA = gae.prefix +prefixdir=$(prefix)/modules/wsgi +gae.prefix: $(top_builddir)/config.status + echo ${GAE_PREFIX} >gae.prefix + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +noinst_PROGRAMS = client-test + +if WANT_GAE +TESTS = util-test wsgi-test wiring-test http-test server-test gae-test +else +TESTS = util-test wsgi-test wiring-test http-test server-test +endif + +endif diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/app.yaml b/sca-cpp/branches/gcc-4.4/modules/wsgi/app.yaml new file mode 100644 index 0000000000..bc70aceced --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/app.yaml @@ -0,0 +1,50 @@ +# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+application: sca-wsgi
+version: 1
+runtime: python
+api_version: 1
+skip_files:
+- ^(.*/)?app\.yaml
+- ^(.*/)?app\.yml
+- ^(.*/)?index\.yaml
+- ^(.*/)?index\.yml
+- ^(.*/)?#.*#
+- ^(.*/)?.*~
+- ^(.*/)?.*\.py[co]
+- ^(.*/)?.*/RCS/.*
+- ^(.*/)?\..*
+- ^(.*/)?.*-test$
+- ^(.*/)?.*\.cpp$
+- ^(.*/)?.*\.o$
+- ^(.*/)?core$
+- ^(.*/)?.*\.out$
+- ^(.*/)?.*\.log$
+- ^(.*/)?Makefile.*
+- ^(.*/)?tmp/.*
+- ^(.*/)?wsgi-start
+- ^(.*/)?wsgi-stop
+
+handlers:
+- url: /(.*\.(html|js|png))
+ static_files: htdocs/\1
+ upload: htdocs/(.*\.(html|js|png))
+
+- url: /.*
+ script: composite.py
+
diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/atom-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/atom-test.py new file mode 100755 index 0000000000..61435bf438 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/atom-test.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# 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. + +# Test ATOM data conversion functions + +import unittest +from elemutil import * +from atomutil import * + +itemEntry = \ + "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \ + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">item</title>" \ + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" \ + "<content type=\"application/xml\">" \ + "<item>" \ + "<name>Apple</name><price>$2.99</price>" \ + "</item>" \ + "</content>" \ + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\" />" \ + "</entry>\n" + +incompleteEntry = \ + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title>item</title><content type=\"text/xml\">" \ + "<Item xmlns=\"http://services/\">" \ + "<name xmlns=\"\">Orange</name>" \ + "<price xmlns=\"\">3.55</price>" \ + "</Item>" \ + "</content>" \ + "</entry>" + +completedEntry = \ + "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \ + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">item</title>" \ + "<id />" \ + "<content type=\"application/xml\">" \ + "<Item xmlns=\"http://services/\">" \ + "<name xmlns=\"\">Orange</name>" \ + "<price xmlns=\"\">3.55</price>" \ + "</Item>" \ + "</content><link href=\"\" />" \ + "</entry>\n" + +def testEntry(): + i = (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99")) + a = ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", i) + s = writeATOMEntry(a); + assert car(s) == itemEntry + + a2 = readATOMEntry((itemEntry,)) + s2 = writeATOMEntry(a2) + assert car(s2) == itemEntry + + a3 = readATOMEntry((incompleteEntry,)) + s3 = writeATOMEntry(a3) + assert car(s3) == completedEntry + return True + +emptyFeed = \ + "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \ + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">Feed</title>" \ + "<id>1234</id>" \ + "</feed>\n" + +itemFeed = \ + "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \ + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">Feed</title>" \ + "<id>1234</id>" \ + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">item</title>" \ + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" \ + "<content type=\"application/xml\">" \ + "<item>" \ + "<name>Apple</name><price>$2.99</price>" \ + "</item>" \ + "</content>" \ + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\" />" \ + "</entry>" \ + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" \ + "<title type=\"text\">item</title>" \ + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</id>" \ + "<content type=\"application/xml\">" \ + "<item>" \ + "<name>Orange</name><price>$3.55</price>" \ + "</item>" \ + "</content>" \ + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c\" />" \ + "</entry>" \ + "</feed>\n" + +def testFeed(): + s = writeATOMFeed(("Feed", "1234")) + assert car(s) == emptyFeed + + a2 = readATOMFeed((emptyFeed,)) + s2 = writeATOMFeed(a2) + assert car(s2) == emptyFeed + + i3 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + (element, "'item", (element, "'name", "Orange"), (element, "'price", "$3.55")))) + a3 = cons("Feed", cons("1234", i3)) + s3 = writeATOMFeed(a3) + assert car(s3) == itemFeed + + i4 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + valueToElement(("'item", ("'name", "Apple"), ("'price", "$2.99")))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + valueToElement(("'item", ("'name", "Orange"), ("'price", "$3.55"))))) + a4 = cons("Feed", cons("1234", i4)) + s4 = writeATOMFeed(a4) + assert car(s4) == itemFeed + + a5 = readATOMFeed((itemFeed,)); + s5 = writeATOMFeed(a5); + assert car(s5) == itemFeed + + i6 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + (("'name", "Apple"), ("'price", "$2.99"))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + (("'name", "Orange"), ("'price", "$3.55")))) + a6 = cons("Feed", cons("1234", i6)) + s6 = writeATOMFeed(feedValuesToElements(a6)) + assert car(s6) == itemFeed + + return True + +if __name__ == "__main__": + print "Testing..." + testEntry() + testFeed() + print "OK" + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/atomutil.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/atomutil.py new file mode 100644 index 0000000000..0ec83ca9e0 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/atomutil.py @@ -0,0 +1,103 @@ +# 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. + +# ATOM data conversion functions + +from util import * +from elemutil import * +from xmlutil import * + +# Convert a list of elements to a list of values representing an ATOM entry +def entryElementsToValues(e): + lt = filter(selector((element, "'title")), e) + t = "" if isNil(lt) else elementValue(car(lt)) + li = filter(selector((element, "'id")), e) + i = "" if isNil(li) else elementValue(car(li)) + lc = filter(selector((element, "'content")), e) + return (t, i, cadr(elementChildren(car(lc)))) + +# Convert a list of elements to a list of values representing ATOM entries +def entriesElementsToValues(e): + if isNil(e): + return e + return cons(entryElementsToValues(car(e)), entriesElementsToValues(cdr(e))) + +# Convert a list of strings to a list of values representing an ATOM entry +def readATOMEntry(l): + e = readXML(l) + if isNil(e): + return () + return entryElementsToValues(car(e)) + +# Convert a list of values representy an ATOM entry to a value +def entryValue(e): + v = elementsToValues((caddr(e),)) + return cons(car(e), (cadr(e), cdr(car(v)))) + +# Convert a list of strings to a list of values representing an ATOM feed +def readATOMFeed(l): + f = readXML(l) + if isNil(f): + return () + t = filter(selector((element, "'title")), car(f)) + i = filter(selector((element, "'id")), car(f)) + e = filter(selector((element, "'entry")), car(f)) + if isNil(e): + return (elementValue(car(t)), elementValue(car(i))) + return cons(elementValue(car(t)), cons(elementValue(car(i)), entriesElementsToValues(e))) + +# Convert a list of values representy an ATOM entry to a list of elements +def entryElement(l): + return (element, "'entry", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + (element, "'title", (attribute, "'type", "text"), car(l)), + (element, "'id", cadr(l)), + (element, "'content", (attribute, "'type", "application/xml"), caddr(l)), + (element, "'link", (attribute, "'href", cadr(l)))) + +# Convert a list of values representing ATOM entries to a list of elements +def entriesElements(l): + if isNil(l): + return l + return cons(entryElement(car(l)), entriesElements(cdr(l))) + +# Convert a list of values representing an ATOM entry to an ATOM entry +def writeATOMEntry(l): + return writeXML((entryElement(l),), True) + +# Convert a list of values representing an ATOM feed to an ATOM feed +def writeATOMFeed(l): + f = (element, "'feed", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + (element, "'title", (attribute, "'type", "text"), car(l)), + (element, "'id", cadr(l))) + if isNil(cddr(l)): + return writeXML((f,), True) + fe = f + entriesElements(cddr(l)) + return writeXML((fe,), True) + +# Convert an ATOM entry containing a value to an ATOM entry containing an item element +def entryValuesToElements(v): + return cons(car(v), cons(cadr(v), valuesToElements((cons("'item", caddr(v)),)))) + +# Convert an ATOM feed containing values to an ATOM feed containing elements +def feedValuesToElementsLoop(v): + if isNil(v): + return v + return cons(entryValuesToElements(car(v)), feedValuesToElementsLoop(cdr(v))) + +def feedValuesToElements(v): + return cons(car(v), cons(cadr(v), feedValuesToElementsLoop(cddr(v)))) + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.cpp b/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.cpp new file mode 100644 index 0000000000..da4fff973b --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.cpp @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "../server/client-test.hpp" + +int main(const int argc, const char** argv) { + tuscany::cout << "Testing..." << tuscany::endl; + tuscany::server::testURI = argc < 2? "http://localhost:8090/wsgi" : argv[1]; + tuscany::server::testBlobs = false; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.py new file mode 100644 index 0000000000..47e6cf4bda --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/client-test.py @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# JSON-RPC test case + +def echo(x, ref): + return ref("echo", x) + +# ATOMPub test case + +def get(id, ref): + return ref("get", id) + +def post(collection, item, ref): + return ref("post", collection, item) + +def put(id, item, ref): + return ref("put", id, item) + +def delete(id, ref): + return ref("delete", id) diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/composite.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/composite.py new file mode 100755 index 0000000000..4be44c3101 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/composite.py @@ -0,0 +1,220 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Composite deployment and integration with WSGI + +from wsgiref.simple_server import make_server +from wsgiref.handlers import CGIHandler +from wsgiref.util import request_uri +from wsgiref.util import FileWrapper +from os import environ +import os.path +import hashlib +from sys import stderr, argv +from util import * +from scdl import * +from atomutil import * +from jsonutil import * + +# Cache the deployed components between requests +comps = None + +# Return the path of an HTTP request +def requestPath(e): + return e.get("PATH_INFO", "") + +# Return the method of an HTTP request +def requestMethod(e): + return e.get("REQUEST_METHOD", "") + +# Return the method of an HTTP request +def requestContentType(e): + return e.get("CONTENT_TYPE", "") + +# Return the request body input stream +def requestBody(e): + i = e.get("wsgi.input", None) + if i == None: + return () + l = int(e.get("CONTENT_LENGTH", "0")) + return (i.read(l),) + +def requestIfNoneMatch(e): + return e.get("HTTP_IF_NONE_MATCH", ""); + +# Hash a list of strings into an MD5 signature +def md5update(md, s): + if isNil(s): + return md.hexdigest() + md.update(car(s)) + return md5update(md, cdr(s)) + +def md5(s): + return md5update(hashlib.md5(), s) + +# Return an HTTP success result +def result(e, r, st, h = (), b = None): + if st == 201: + r("201 Created", list(h)) + return () + + if st == 200: + if b == None: + r("200 OK", list(h)) + return () + + # Handle etags to minimize bandwidth usage + md = md5(b) + if (md == requestIfNoneMatch(e)): + r("304 Not Modified", list((("Etag", md), ("Expires", "Tue, 01 Jan 1980 00:00:00 GMT")))) + return () + r("200 OK", list(h + (("Etag", md), ("Expires", "Tue, 01 Jan 1980 00:00:00 GMT")))) + return b + + return failure(e, r, 500) + +# Return an HTTP failure result +def failure(e, r, st): + s = "404 Not Found" if st == 404 else str(st) + " " + "Internal Server Error" + r(s, list((("Content-type", "text/html"),))) + return ("<html><head><title>"+ s + "</title></head><body><h1>" + s[4:] + "</h1></body></html>",) + +# Return a static file +def fileresult(e, r, ct, f): + + # Read the file, return a 404 if not found + p = "htdocs" + f + if not os.path.exists(p): + return failure(e, r, 404) + c = tuple(FileWrapper(open("htdocs" + f))) + + # Handle etags to minimize bandwidth usage + md = md5(c) + r("200 OK", list((("Content-type", ct),("Etag", md)))) + return c + +# Converts the args received in a POST to a list of key value pairs +def postArgs(a): + if isNil(a): + return ((),) + l = car(a); + return cons(l, postArgs(cdr(a))) + +# WSGI application function +def application(e, r): + m = requestMethod(e) + fpath = requestPath(e) + + # Debug hook + if fpath == "/debug": + return result(e, r, 200, (("Content-type", "text/plain"),), ("Debug",)) + + # Serve static files + if m == "GET": + if fpath.endswith(".html"): + return fileresult(e, r, "text/html", fpath) + if fpath.endswith(".js"): + return fileresult(e, r, "application/x-javascript", fpath) + if fpath.endswith(".png"): + return fileresult(e, r, "image/png", fpath) + + # Find the requested component + path = tokens(fpath) + uc = uriToComponent(path, comps) + uri = car(uc) + if uri == None: + return failure(e, r, 404) + comp = cadr(uc) + + # Call the requested component function + id = path[len(uri):] + if m == "GET": + v = comp("get", id) + + # Write returned content-type / content pair + if not isinstance(cadr(v), basestring): + return result(e, r, 200, (("Content-type", car(v)),), cadr(v)) + + # Write an ATOM feed or entry + if isNil(id): + return result(e, r, 200, (("Content-type", "application/atom+xml;type=feed"),), writeATOMFeed(feedValuesToElements(v))) + return result(e, r, 200, (("Content-type", "application/atom+xml;type=entry"),), writeATOMEntry(entryValuesToElements(v))) + + if m == "POST": + ct = requestContentType(e) + + # Handle a JSON-RPC function call + if ct.find("application/json-rpc") != -1 or ct.find("text/plain") != -1: + json = elementsToValues(readJSON(requestBody(e))) + args = postArgs(json) + jid = cadr(assoc("'id", args)) + func = funcName(cadr(assoc("'method", args))) + params = cadr(assoc("'params", args)) + v = comp(func, *params) + return result(e, r, 200, (("Content-type", "application/json-rpc"),), jsonResult(jid, v)) + + # Handle an ATOM entry POST + if ct.find("application/atom+xml") != -1: + ae = entryValue(readATOMEntry(requestBody(e))) + v = comp("post", id, ae) + if isNil(v): + return failure(e, r, 500) + return result(e, r, 201, (("Location", request_uri(e) + "/" + "/".join(v)),)) + return failure(e, r, 500) + + if m == "PUT": + # Handle an ATOM entry PUT + ae = entryValue(readATOMEntry(requestBody(e))) + v = comp("put", id, ae) + if v == False: + return failure(e, r, 404) + return result(e, r, 200) + + if m == "DELETE": + v = comp("delete", id) + if v == False: + return failure(e, r, 404) + return result(e, r, 200) + + return failure(e, r, 500) + +# Return the WSGI server type +def serverType(e): + return e.get("SERVER_SOFTWARE", "") + +def main(): + # Read the deployed composite and evaluate the configured components + global comps + if comps == None: + domain = "domain.composite" if os.path.exists("domain.composite") else "domain-test.composite" + comps = evalComponents(components(parse(domain))) + + # Handle the WSGI request with the WSGI runtime + st = serverType(environ) + if st.find("App Engine") != -1 or st.find("Development") != -1: + from google.appengine.ext.webapp.util import run_wsgi_app + run_wsgi_app(application) + elif st != "": + CGIHandler().run(application) + else: + make_server("", int(argv[1]), application).serve_forever() + +# Run the WSGI application +if __name__ == "__main__": + main() + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/domain-test.composite b/sca-cpp/branches/gcc-4.4/modules/wsgi/domain-test.composite new file mode 100644 index 0000000000..9c44ebf5d8 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/domain-test.composite @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="wsgi-test"> + <t:implementation.python script="server-test.py"/> + <service name="test"> + <t:binding.http uri="wsgi"/> + </service> + </component> + + <component name="client-test"> + <t:implementation.python script="client-test.py"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="wsgi-test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/elemutil.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/elemutil.py new file mode 100644 index 0000000000..ad971ba6ba --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/elemutil.py @@ -0,0 +1,168 @@ +# 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. + +# Functions to help represent data as lists of elements and attributes + +from util import * + +element = "'element" +attribute = "'attribute" +atsign = "'@" + +# Return true if a value is an element +def isElement(v): + if not isList(v) or isNil(v) or v == None or car(v) != element: + return False + return True + +# Return true if a value is an attribute +def isAttribute(v): + if not isList(v) or isNil(v) or v == None or car(v) != attribute: + return False + return True + +# Return the name of attribute +def attributeName(l): + return cadr(l) + +# Return the value of attribute +def attributeValue(l): + return caddr(l) + +# Return the name of an element +def elementName(l): + return cadr(l) + +# Return true if an element has children +def elementHasChildren(l): + return not isNil(cddr(l)) + +# Return the children of an element +def elementChildren(l): + return cddr(l) + +# Return true if an element has a value +def elementHasValue(l): + r = reverse(l) + if isSymbol(car(r)): + return False + if isList(car(r)) and not isNil(car(r)) and isSymbol(car(car(r))): + return False + return True + +# Return the value of an element +def elementValue(l): + return car(reverse(l)) + +# Convert an element to a value +def elementToValueIsList(v): + if not isList(v): + return False + return isNil(v) or not isSymbol(car(v)) + +def elementToValue(t): + if isTaggedList(t, attribute): + return (atsign + attributeName(t)[1:], attributeValue(t)) + if isTaggedList(t, element): + if elementHasValue(t): + if not elementToValueIsList(elementValue(t)): + return (elementName(t), elementValue(t)) + return cons(elementName(t), (elementsToValues(elementValue(t)),)) + return cons(elementName(t), elementsToValues(elementChildren(t))) + if not isList(t): + return t + return elementsToValues(t) + +# Convert a list of elements to a list of values +def elementToValueIsSymbol(v): + if not isList(v): + return False + if (isNil(v)): + return False + if not isSymbol(car(v)): + return False + return True + +def elementToValueGroupValues(v, l): + if isNil(l) or not elementToValueIsSymbol(v) or not elementToValueIsSymbol(car(l)): + return cons(v, l) + if car(car(l)) != car(v): + return cons(v, l) + if not elementToValueIsList(cadr(car(l))): + g = (car(v), (cdr(v), cdr(car(l)))) + return elementToValueGroupValues(g, cdr(l)) + g = (car(v), cons(cdr(v), cadr(car(l)))) + return elementToValueGroupValues(g, cdr(l)) + +def elementsToValues(e): + if isNil(e): + return e + return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e))) + +# Convert a value to an element +def valueToElement(t): + if isList(t) and not isNil(t) and isSymbol(car(t)): + n = car(t) + v = cadr(t) + if not isList(v): + if n[0:2] == atsign: + return (attribute, n[1:], v) + return (element, n, v) + if isNil(v) or not isSymbol(car(v)): + return cons(element, cons(n, (valuesToElements(v),))) + return cons(element, cons(n, valuesToElements(cdr(t)))) + if not isList(t): + return t + return valuesToElements(t) + +# Convert a list of values to a list of elements +def valuesToElements(l): + if isNil(l): + return l + return cons(valueToElement(car(l)), valuesToElements(cdr(l))) + +# Return a selector lambda function which can be used to filter elements +def evalSelect(s, v): + if isNil(s): + return True + if isNil(v): + return False + if car(s) != car(v): + return False + return evalSelect(cdr(s), cdr(v)) + +def selector(s): + return lambda v: evalSelect(s, v) + +# Return the value of the attribute with the given name +def namedAttributeValue(name, l): + f = filter(lambda v: isAttribute(v) and attributeName(v) == name, l) + if isNil(f): + return None + return caddr(car(f)) + +# Return child elements with the given name +def namedElementChildren(name, l): + return filter(lambda v: isElement(v) and elementName(v) == name, l) + +# Return the child element with the given name +def namedElementChild(name, l): + f = namedElementChildren(name, l) + if isNil(f): + return None + return car(f) + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-start b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-start new file mode 100755 index 0000000000..69882750aa --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-start @@ -0,0 +1,29 @@ +#!/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. + +# Start Google AppEngine server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +port=$2 + +python_prefix=`cat $here/../python/python.prefix` +gae_prefix=`cat $here/gae.prefix` +cd $root +$python_prefix/bin/python $gae_prefix/dev_appserver.py -p $port $root & + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-stop b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-stop new file mode 100755 index 0000000000..64e7031058 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-stop @@ -0,0 +1,29 @@ +#!/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. + +# Stop Google AppEngine server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +port=$2 + +python_prefix=`cat $here/../python/python.prefix` +gae_prefix=`cat $here/gae.prefix` +py="$python_prefix/bin/python $gae_prefix/dev_appserver.py -p $port $root" + +kill `ps -ef | grep -v grep | grep "${py}" | awk '{ print $2 }'` diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-test new file mode 100755 index 0000000000..1791a830ca --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/gae-test @@ -0,0 +1,30 @@ +#!/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 +./gae-start target 8090 2>/dev/null +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +./gae-stop target 8090 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/entry.xml b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/entry.xml new file mode 100644 index 0000000000..5796cd655f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/entry.xml @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='UTF-8'?> +<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111" /></entry> diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/feed.xml b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/feed.xml new file mode 100644 index 0000000000..d15b265f15 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/feed.xml @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='UTF-8'?> +<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111" /></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222" /></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item>('name', 'Pear')<javaClass>services.Item</javaClass><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333" /></entry></feed> diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/index.html b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/index.html new file mode 100644 index 0000000000..cd25bf17b3 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/index.html @@ -0,0 +1 @@ +<html><body><h1>It works!</h1></body></html>
\ No newline at end of file diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-request.txt b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-request.txt new file mode 100644 index 0000000000..b4bd07fc46 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-request.txt @@ -0,0 +1 @@ +{"id":1,"method":"echo","params":["Hello"]} diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-result.txt b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-result.txt new file mode 100644 index 0000000000..121bf74902 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/htdocs/json-result.txt @@ -0,0 +1 @@ +{"id":1,"result":"Hello"}
\ No newline at end of file diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test new file mode 100755 index 0000000000..6676f6514c --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test @@ -0,0 +1,39 @@ +#!/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. + +uri=$1 +if [ "$uri" = "" ]; then + uri="http://localhost:8090" +fi + +# Setup +mkdir -p tmp +./wsgi-start target 8090 2>/dev/null +sleep 2 + +# Test JSON-RPC +here=`readlink -f $0`; here=`dirname $here` +python_prefix=`cat $here/../python/python.prefix` +$python_prefix/bin/python http-test.py +rc=$? + +# Cleanup +./wsgi-stop target 8090 +sleep 2 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test.py new file mode 100755 index 0000000000..87a8780fb3 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/http-test.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +# 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. + +# HTTP client proxy functions + +from httputil import * + +def testClient(): + c = mkclient("http://localhost:8090/wsgi") + assert c("echo", "Hey") == "Hey" + return True + +if __name__ == "__main__": + print "Testing..." + testClient() + print "OK" + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/httputil.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/httputil.py new file mode 100644 index 0000000000..77d19eabe2 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/httputil.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# 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. + +# HTTP client proxy functions + +from httplib import HTTPConnection, HTTPSConnection +from urlparse import urlparse +from StringIO import StringIO +import os.path +from string import strip +from base64 import b64encode +from sys import stderr +from util import * +from atomutil import * +from jsonutil import * + +id = 1 + +# Make a callable HTTP client +class client: + def __init__(self, uri): + self.uri = urlparse(uri) + + def __call__(self, func, *args): + + # Connect to the configured URI + print >> stderr, "Client POST", self.uri.geturl() + c = None + headers = {"Content-type": "application/json-rpc"} + if self.uri.scheme == "https": + + # With HTTPS, use a cerficate or HTTP basic authentication + if os.path.exists("server.key"): + c = HTTPSConnection(self.uri.hostname, 443 if self.uri.port == None else self.uri.port, "server.key", "server.crt") + else: + c = HTTPSConnection(self.uri.hostname, 443 if self.uri.port == None else self.uri.port) + + # For HTTP basic authentication the user and password are + # provided by htpasswd.py + import htpasswd + auth = 'Basic ' + b64encode(htpasswd.user + ':' + htpasswd.passwd) + headers["Authorization"] = auth + else: + c = HTTPConnection(self.uri.hostname, 80 if self.uri.port == None else self.uri.port) + + # POST the JSON-RPC request + global id + req = StringIO() + writeStrings(jsonRequest(id, func, args), req) + id = id + 1 + c.request("POST", self.uri.path, req.getvalue(), headers) + res = c.getresponse() + print >> stderr, "Client status", res.status + if res.status != 200: + return None + return jsonResultValue((res.read(),)) + + def __repr__(self): + return repr((self.uri,)) + +def mkclient(uri): + return client(uri) + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/json-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/json-test.py new file mode 100755 index 0000000000..2f2a755bff --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/json-test.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# 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. + +# Test JSON data conversion functions + +import unittest +from elemutil import * +from jsonutil import * + +def testJSON(): + ad = ((attribute, "'city", "san francisco"), (attribute, "'state", "ca")) + ac = ((element, "'id", "1234"), (attribute, "'balance", 1000)) + cr = ((attribute, "'name", "jdoe"), cons(element, cons("'address", ad)), cons(element, cons("'account", ac))) + c = (cons(element, cons("'customer", cr)),) + s = writeJSON(c); + assert car(s) == "{\"customer\":{\"account\":{\"@balance\":1000,\"id\":\"1234\"},\"@name\":\"jdoe\",\"address\":{\"@city\":\"san francisco\",\"@state\":\"ca\"}}}" + + phones = ("408-1234", "650-1234") + l = ((element, "'phones", phones), (element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1")) + s2 = writeJSON(l); + assert car(s2) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"@firstName\":\"test1\",\"lastName\":\"test\\ttab\"}" + + r = readJSON(s2) + assert r == ((element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1"), (element, "'phones", phones)) + w = writeJSON(r) + assert car(w) == "{\"lastName\":\"test\\ttab\",\"@firstName\":\"test1\",\"phones\":[\"408-1234\",\"650-1234\"]}" + + l4 = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),) + s4 = writeJSON(valuesToElements(l4)) + assert car(s4) == "{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}" + + r4 = elementsToValues(readJSON(s4)) + assert r4 == (("'ns1:echoString", ("'text", 'Hello World!'), ("'@xmlns:ns1", 'http://ws.apache.org/axis2/services/echo')),) + return True + +def testJSONRPC(): + return True + +if __name__ == "__main__": + print "Testing..." + testJSON() + testJSONRPC() + print "OK" + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/jsonutil.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/jsonutil.py new file mode 100644 index 0000000000..ae7839df57 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/jsonutil.py @@ -0,0 +1,140 @@ +# 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. + +# JSON data conversion functions + +try: + import json +except: + from django.utils import simplejson as json + +from StringIO import StringIO +from util import * +from elemutil import * + +# Return true if a list represents a JS array +def isJSArray(l): + if isNil(l): + return True + v = car(l) + if isSymbol(v): + return False + if isList(v): + if not isNil(v) and isSymbol(car(v)): + return False + return True + +# Converts JSON properties to values +def jsPropertiesToValues(propertiesSoFar, o, i): + if isNil(i): + return propertiesSoFar + p = car(i) + jsv = o[p] + v = jsValToValue(jsv) + + if isinstance(p, basestring): + n = str(p) + if n[0:1] == "@": + return jsPropertiesToValues(cons((attribute, "'" + n[1:], v), propertiesSoFar), o, cdr(i)) + if isList(v) and not isJSArray(v): + return jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i)) + return jsPropertiesToValues(cons((element, "'" + n, v), propertiesSoFar), o, cdr(i)) + return jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i)) + +# Converts a JSON val to a value +def jsValToValue(jsv): + if isinstance(jsv, dict): + return jsPropertiesToValues((), jsv, tuple(jsv.keys())) + if isList(jsv): + return jsPropertiesToValues((), jsv, tuple(reversed(range(0, len(jsv))))) + if isinstance(jsv, basestring): + return str(jsv) + return jsv + +# Convert a list of strings representing a JSON document to a list of values +def readJSON(l): + s = StringIO() + writeStrings(l, s) + val = json.loads(s.getvalue()) + return jsValToValue(val) + +# Convert a list of values to JSON array elements +def valuesToJSElements(a, l, i): + if isNil(l): + return a + pv = valueToJSVal(car(l)) + a[i] = pv + return valuesToJSElements(a, cdr(l), i + 1) + +# Convert a value to a JSON value +def valueToJSVal(v): + if not isList(v): + return v + if isJSArray(v): + return valuesToJSElements(list(range(0, len(v))), v, 0) + return valuesToJSProperties({}, v) + +# Convert a list of values to JSON properties +def valuesToJSProperties(o, l): + if isNil(l): + return o + token = car(l) + if isTaggedList(token, attribute): + pv = valueToJSVal(attributeValue(token)) + o["@" + attributeName(token)[1:]] = pv + elif isTaggedList(token, element): + if elementHasValue(token): + pv = valueToJSVal(elementValue(token)) + o[elementName(token)[1:]] = pv + else: + child = {} + o[elementName(token)[1:]] = child + valuesToJSProperties(child, elementChildren(token)) + return valuesToJSProperties(o, cdr(l)) + +# Convert a list of values to a list of strings representing a JSON document +def writeJSON(l): + jsv = valuesToJSProperties({}, l) + s = json.dumps(jsv, separators=(',',':')) + return (s,) + +# Convert a list + params to a JSON-RPC request +def jsonRequest(id, func, params): + r = (("'id", id), ("'method", func), ("'params", params)) + return writeJSON(valuesToElements(r)) + +# Convert a value to a JSON-RPC result +def jsonResult(id, val): + return writeJSON(valuesToElements((("'id", id), ("'result", val)))) + +# Convert a JSON-RPC result to a value +def jsonResultValue(s): + jsres = readJSON(s) + res = elementsToValues(jsres) + val = cadr(assoc("'result", res)) + if isList(val) and not isJSArray(val): + return (val,) + return val + +# Return a portable function name from a JSON-RPC function name +def funcName(f): + if len(f) > 7 and f.find("system.") == 0: + return f[7:] + if len(f) > 8 and f.find("Service.") == 0: + return f[8:] + return f + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/scdl.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/scdl.py new file mode 100644 index 0000000000..af332d0249 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/scdl.py @@ -0,0 +1,167 @@ +# 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. + +# SCDL parsing functions + +from xml.etree.cElementTree import iterparse +from util import * +from httputil import * + +# Element tree utility functions, used to parse SCDL documents +def parse(file): + return map(lambda x: x, iterparse(file, events=("start", "end"))) + +def evt(e): + return car(e) + +def elt(e): + return cadr(e) + +def att(e): + return elt(e).attrib + +def text(e): + return elt(e).text + +def match(e, ev, tag): + return evt(e) == ev and elt(e).tag.find("}" + tag) != -1 + +# Make a callable component +class component: + def __init__(self, name, impl, svcs, refs, props): + self.name = name + self.impl = impl + self.mod = None + self.svcs = svcs + self.refs = refs + self.props = props + self.proxies = () + + def __call__(self, func, *args): + return self.mod.__getattribute__(func)(*(args + self.proxies)) + + def __repr__(self): + return repr((self.name, self.impl, self.mod, self.svcs, self.refs, self.props, self.proxies)) + +def mkcomponent(name, impl, svcs, refs, props): + return component(name, impl, svcs, refs, props) + +# Return the Python module name of a component implementation +def implementation(e): + if len(e) == 0 or match(car(e), "end", "component") == True: + return "" + if match(car(e), "start", "implementation.python") == False: + return implementation(cdr(e)) + if "script" in att(car(e)): + s = att(car(e))["script"] + return s[0:len(s) - 3] + return None + +# Return the URI of a binding under a SCDL service or reference element +def binding(e): + if len(e) == 0 or match(car(e), "end", "reference") == True or match(car(e), "end", "service") == True: + return () + if match(car(e), "start", "binding.") == False: + return binding(cdr(e)) + return att(car(e))["uri"] + +# Return the list of references under a SCDL component element +def references(e): + if len(e) == 0 or match(car(e), "end", "component") == True: + return () + if match(car(e), "start", "reference") == False: + return references(cdr(e)) + if "target" in att(car(e)): + return cons(car(tokens(att(car(e))["target"])), references(cdr(e))) + return cons(binding(e), references(cdr(e))) + +# Return the list of properties under a SCDL component element +def properties(e): + if len(e) == 0 or match(car(e), "end", "component") == True: + return () + if match(car(e), "start", "property") == False: + return properties(cdr(e)) + return cons(text(car(e)), properties(cdr(e))) + +# Return the list of services under a SCDL component element +def services(e): + if len(e) == 0 or match(car(e), "end", "component") == True: + return () + if match(car(e), "start", "service") == False: + return services(cdr(e)) + return cons(tokens(binding(e)), services(cdr(e))) + +# Return the name attribute of a SCDL element +def name(e): + return att(car(e))["name"] + +# Return the list of components under a SCDL composite element +def components(e): + if len(e) == 0: + return () + if match(car(e), "start", "component") == False: + return components(cdr(e)) + n = name(e) + return cons(mkcomponent(n, implementation(e), cons(("components", n), services(e)), references(e), properties(e)), components(cdr(e))) + +# Find a component with a given name +def nameToComponent(name, comps): + if comps == (): + return None + if car(comps).name == name: + return car(comps) + return nameToComponent(name, cdr(comps)) + +# Find the URI matching a given URI in a list of service URIs +def matchingURI(u, svcs): + if svcs == (): + return None + if car(svcs) == u[0:len(car(svcs))]: + return car(svcs) + return matchingURI(u, cdr(svcs)) + +# Return the (service URI, component) pair matching a given URI +def uriToComponent(u, comps): + if comps == (): + return (None, None) + m = matchingURI(u, car(comps).svcs) + if m != None: + return (m, car(comps)) + return uriToComponent(u, cdr(comps)) + +# Evaluate a reference, return a proxy to the resolved component or an +# HTTP client configured with the reference target uri +def evalReference(r, comps): + if r.startswith("http://") or r.startswith("https://"): + return mkclient(r) + return nameToComponent(r, comps) + +# Evaluate a component, resolve its implementation and references +def evalComponent(comp, comps): + comp.mod = __import__(comp.impl) + + # Make a list of proxy lambda functions for the component references and properties + # A reference proxy is the callable lambda function of the component wired to the reference + # A property proxy is a lambda function that returns the value of the property + comp.proxies = tuple(map(lambda r: evalReference(r, comps), comp.refs)) + tuple(map(lambda v: lambda: v, comp.props)) + + return comp + +# Evaluate a list of components +def evalComponents(comps): + return tuple(map(lambda c: evalComponent(c, comps), comps)) + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test new file mode 100755 index 0000000000..9bd862c53a --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test @@ -0,0 +1,30 @@ +#!/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 +./wsgi-start target 8090 2>/dev/null +sleep 2 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +./wsgi-stop target 8090 +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test.py new file mode 100644 index 0000000000..9e084a3f92 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/server-test.py @@ -0,0 +1,44 @@ +# 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. + +# JSON-RPC test case + +def echo(x): + return x + +# ATOMPub test case + +def get(id): + if id == ("index.html",): + return ("text/plain", ("It works!",)) + if id == (): + return ("Sample Feed", "123456789", + ("Item", "111", (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99))), + ("Item", "222", (("'javaClass", "services.Item"), ("'name", "Orange"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 3.55))), + ("Item", "333", (("'javaClass", "services.Item"), ("name", "Pear"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 1.55)))) + + entry = (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99)) + return ("Item", id[0], entry) + +def post(collection, item): + return ("123456789",) + +def put(id, item): + return True + +def delete(id): + return True diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/util-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/util-test new file mode 100755 index 0000000000..a1c54e80f1 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/util-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. + +# Run Python util test cases +here=`readlink -f $0`; here=`dirname $here` +python_prefix=`cat $here/../python/python.prefix` + +$python_prefix/bin/python xml-test.py +rc=$? +if [ "$rc" = "0" ]; then + $python_prefix/bin/python atom-test.py + rc=$? +fi +if [ "$rc" = "0" ]; then + $python_prefix/bin/python json-test.py + rc=$? +fi + +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/util.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/util.py new file mode 100644 index 0000000000..d945c7bdef --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/util.py @@ -0,0 +1,83 @@ +# 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. + +# Simple utility functions + +# Scheme-like lists +def cons(a, b): + return (a,) + b + +def car(l): + return l[0] + +def cdr(l): + return l[1:] + +def cadr(l): + return car(cdr(l)) + +def cddr(l): + return cdr(cdr(l)) + +def caddr(l): + return car(cddr(l)) + +def reverse(l): + r = list(l) + r.reverse() + return tuple(r) + +def isNil(l): + return l == () + +def isSymbol(v): + return isinstance(v, basestring) and v[0:1] == "'" + +def isList(v): + if getattr(v, '__iter__', False) == False: + return False + if isinstance(v, basestring) or isinstance(v, dict): + return False + return True + +def isTaggedList(v, t): + return isList(v) and not isNil(v) and car(v) == t + +# Scheme-like associations +def assoc(k, l): + if l == (): + return None + + if k == car(car(l)): + return car(l) + return assoc(k, cdr(l)) + +# Currying / partial function application +def curry(f, *args): + return lambda *a: f(*(args + a)) + +# Split a path into a list of segments +def tokens(path): + return tuple(filter(lambda s: len(s) != 0, path.split("/"))) + +# Write a list of strings to a stream +def writeStrings(l, os): + if l == (): + return os + os.write(car(l)) + return writeStrings(cdr(l), os) + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/wiring-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/wiring-test new file mode 100755 index 0000000000..f1d0e8332f --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/wiring-test @@ -0,0 +1,75 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` +uri=$1 +if [ "$uri" = "" ]; then + uri="http://localhost:8090" +fi + +# Setup +mkdir -p tmp +./wsgi-start target 8090 2>/dev/null +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl $uri/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl $uri/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +./wsgi-stop target 8090 +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-start b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-start new file mode 100755 index 0000000000..d020f3da14 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-start @@ -0,0 +1,28 @@ +#!/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. + +# Start WSGI server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +port=$2 + +python_prefix=`cat $here/../python/python.prefix` +cd $root +$python_prefix/bin/python composite.py $port & + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-stop b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-stop new file mode 100755 index 0000000000..7e12967adb --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-stop @@ -0,0 +1,28 @@ +#!/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. + +# Stop WSGI server +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +port=$2 + +python_prefix=`cat $here/../python/python.prefix` +py="$python_prefix/bin/python composite.py $port" + +kill `ps -ef | grep -v grep | grep "${py}" | awk '{ print $2 }'` diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-test b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-test new file mode 100755 index 0000000000..3dce709a3b --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/wsgi-test @@ -0,0 +1,71 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +mkdir -p tmp +./wsgi-start target 8090 2>/dev/null +sleep 2 + +# Test HTTP GET +$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +./wsgi-stop target 8090 +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/xml-test.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/xml-test.py new file mode 100755 index 0000000000..f60322bdc1 --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/xml-test.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# 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. + +# Test XML handling functions + +import unittest +from elemutil import * +from xmlutil import * + +customerXML = \ + "<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n" \ + "<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>\n" + +def testElements(): + ad = (("'city", "san francisco"), ("'state", "ca")) + ac1 = (("'id", "1234"), ("'balance", 1000)) + ac2 = (("'id", "6789"), ("'balance", 2000)) + ac3 = (("'id", "4567"), ("'balance", 3000)) + c = (("'customer", ("'name", "jdoe"), cons("'address", ad), ("'account", (ac1, ac2, ac3))),) + e = valuesToElements(c) + v = elementsToValues(e) + assert v == c + s = writeXML(e, True) + assert car(s) == customerXML + + c2 = (("'customer", ("'name", "jdoe"), cons("'address", ad), cons("'account", ac1), cons("'account", ac2), cons("'account", ac3)),) + e2 = valuesToElements(c2); + v2 = elementsToValues(e2); + s2 = writeXML(e2, True) + assert car(s2) == customerXML + + c3 = readXML((customerXML,)) + v3 = elementsToValues(c3) + e3 = valuesToElements(v3) + s3 = writeXML(e3, True) + assert car(s3) == customerXML + return True + +def testValues(): + l = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),) + e = valuesToElements(l) + lx = writeXML(e, True) + x = readXML(lx) + v = elementsToValues(x) + assert v == l + return True + +if __name__ == "__main__": + print "Testing..." + testElements() + testValues() + print "OK" + diff --git a/sca-cpp/branches/gcc-4.4/modules/wsgi/xmlutil.py b/sca-cpp/branches/gcc-4.4/modules/wsgi/xmlutil.py new file mode 100644 index 0000000000..a1bc04629a --- /dev/null +++ b/sca-cpp/branches/gcc-4.4/modules/wsgi/xmlutil.py @@ -0,0 +1,114 @@ +# 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. + +# XML handling functions + +from StringIO import StringIO +from xml.parsers import expat +import xml.etree.ElementTree as et +from util import * +from elemutil import * + +# Read a list of XML attributes +def readAttributes(a): + if a == (): + return a + return cons((attribute, "'" + car(car(a)), cadr(car(a))), readAttributes(cdr(a))) + +# Read an XML element +def readElement(e): + l = (element, "'" + e.tag) + readAttributes(tuple(e.items())) + readElements(tuple(e.getchildren())) + if e.text == None: + return l + return l + (e.text,) + +# Read a list of XML elements +def readElements(l): + if l == (): + return l + return cons(readElement(car(l)), readElements(cdr(l))) + +# Parse a list of strings representing an XML document +class NamespaceParser(et.XMLTreeBuilder): + def __init__(self): + et.XMLTreeBuilder.__init__(self) + self._parser = parser = expat.ParserCreate(None) + parser.DefaultHandlerExpand = self._default + parser.StartElementHandler = self._start + parser.EndElementHandler = self._end + parser.CharacterDataHandler = self._data + try: + parser.buffer_text = 1 + except AttributeError: + pass + try: + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + parser.StartElementHandler = self._start_list + except AttributeError: + pass + +def parseXML(l): + s = StringIO() + writeStrings(l, s) + parser = NamespaceParser() + parser.feed(s.getvalue()) + return parser.close() + +# Read a list of values from a list of strings representing an XML document +def readXML(l): + e = parseXML(l) + return (readElement(e),) + +# Write a list of XML element and attribute tokens +def expandElementValues(n, l): + if isNil(l): + return l + return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l))) + +def writeList(l, xml): + if isNil(l): + return xml + token = car(l) + if isTaggedList(token, attribute): + xml.attrib[attributeName(token)[1:]] = str(attributeValue(token)) + elif isTaggedList(token, element): + if elementHasValue(token): + v = elementValue(token) + if isList(v): + e = expandElementValues(elementName(token), v) + writeList(e, xml) + else: + child = et.Element(elementName(token)[1:]) + writeList(elementChildren(token), child) + xml.append(child) + else: + child = et.Element(elementName(token)[1:]) + writeList(elementChildren(token), child) + xml.append(child) + else: + xml.text = str(token) + writeList(cdr(l), xml) + return xml + +# Convert a list of values to a list of strings representing an XML document +def writeXML(l, xmlTag): + e = writeList(l, []) + if not xmlTag: + return (et.tostring(car(e)),) + return (et.tostring(car(e), "UTF-8") + "\n",) + |