diff options
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/modules')
151 files changed, 19398 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/modules/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/Makefile.am new file mode 100644 index 0000000000..16fe2791f7 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/Makefile.am @@ -0,0 +1,19 @@ +# 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 rss js json scdl http server python opencl java openid oauth wsgi + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am new file mode 100644 index 0000000000..a504adcda5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am @@ -0,0 +1,88 @@ +# 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} + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/http + +dist_mod_SCRIPTS = httpd-conf httpd-addr httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf basic-auth-conf cert-auth-conf form-auth-conf open-auth-conf passwd-auth-conf group-auth-conf cache-conf cache-ssl-conf cache-manifest proxy-conf proxy-base-conf proxy-ssl-conf proxy-balancer-conf proxy-member-conf proxy-ssl-member-conf proxy-ssl-nossl-member-conf alt-host-conf mass-host-conf mass-host-ssl-conf httpd-tunnel-ssl-conf tunnel-ssl-conf httpd-worker-conf httpd-event-conf httpd-loglevel-conf minify-html minify-js minify-css +moddir = $(prefix)/modules/http + +curl_test_SOURCES = curl-test.cpp +curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +curl_get_SOURCES = curl-get.cpp +curl_get_LDFLAGS = -lxml2 -lcurl -lmozjs + +curl_connect_SOURCES = curl-connect.cpp +curl_connect_LDFLAGS = -lxml2 -lcurl -lmozjs + +mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la libmod_tuscany_openauth.la +noinst_DATA = libmod_tuscany_ssltunnel${libsuffix} libmod_tuscany_openauth${libsuffix} + +libmod_tuscany_ssltunnel_la_SOURCES = mod-ssltunnel.cpp +libmod_tuscany_ssltunnel_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_ssltunnel${libsuffix}: + ln -s .libs/libmod_tuscany_ssltunnel${libsuffix} + +libmod_tuscany_openauth_la_SOURCES = mod-openauth.cpp +libmod_tuscany_openauth_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_openauth${libsuffix}: + ln -s .libs/libmod_tuscany_openauth${libsuffix} + +mod_DATA = httpd.prefix httpd-apachectl.prefix httpd-modules.prefix curl.prefix pagespeed.prefix +nobase_dist_mod_DATA = conf/* + +EXTRA_DIST = htdocs/index.html htdocs/login/index.html htdocs/logout/index.html + +httpd.prefix: $(top_builddir)/config.status + echo ${HTTPD_PREFIX} >httpd.prefix +httpd-apachectl.prefix: $(top_builddir)/config.status + echo ${HTTPD_APACHECTL_PREFIX} >httpd-apachectl.prefix +httpd-modules.prefix: $(top_builddir)/config.status + echo ${HTTPD_MODULES_PREFIX} >httpd-modules.prefix +curl.prefix: $(top_builddir)/config.status + echo ${CURL_PREFIX} >curl.prefix + +if WANT_PAGESPEED + +pagespeed.prefix: $(top_builddir)/config.status + echo ${PAGESPEED_PREFIX} >pagespeed.prefix + +else + +pagespeed.prefix: $(top_builddir)/config.status + echo "" >pagespeed.prefix + +endif + +if WANT_MODSECURITY + +modsecurity.prefix: $(top_builddir)/config.status + echo ${MODSECURITY_PREFIX} >modsecurity.prefix + +dist_modsecurity_SCRIPTS = mod-security-conf mod-security-audit-conf +modsecurity_DATA = modsecurity.prefix +modsecuritydir = $(prefix)/modules/http + +endif + +dist_noinst_SCRIPTS = httpd-test http-test proxy-test +noinst_PROGRAMS = curl-test curl-get curl-connect +TESTS = httpd-test http-test proxy-test + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf new file mode 100755 index 0000000000..f6148173b4 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf @@ -0,0 +1,32 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate an alternate host name configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 + +cat >>$root/conf/hostcond.conf <<EOF +# Generated by: alt-host-conf $* +RewriteCond %{HTTP_HOST} !^$host [NC] + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf new file mode 100755 index 0000000000..8710d1fdf7 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf @@ -0,0 +1,69 @@ +#!/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 basic authentication configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then + loc="/" +else + loc="$3" +fi + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: basic-auth-conf $* +# Disallow public access to server resources + +EOF + +# Generate basic authentication configuration +cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: basic-auth-conf $* +# Require clients to present a userid + password for HTTP +# basic authentication +<Location $loc> +AuthType Basic +AuthName "$host" +AuthBasicProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +Require valid-user +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf new file mode 100755 index 0000000000..09ad5dc444 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf @@ -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. + +# Generate a minimal HTTPD cache configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +mkdir -p $root/cache + +cat >>$root/conf/vhost.conf <<EOF +# Generated by: cache-conf $* + +# Enable caching +CacheEnable disk / +CacheQuickHandler Off +CacheRoot $root/cache +CacheHeader On +#CacheDetailHeader On +CacheIgnoreQueryString On +CacheIgnoreCacheControl On +CacheIgnoreHeaders Set-Cookie + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest new file mode 100755 index 0000000000..3752a61dde --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest @@ -0,0 +1,26 @@ +#!/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 cache-manifest.cmf file from a template and the list +# of files in the cached application +root=$1 +shift +sha=`cat $* | shasum | awk '{ print $1 }'` +cat $root/cache-template.cmf | sed -e "s/SHA1/$sha/" >$root/cache-manifest.cmf + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf new file mode 100755 index 0000000000..7a902c37eb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf @@ -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. + +# Generate a minimal HTTPD cache configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +mkdir -p $root/cache + +cat >>$root/conf/vhost-ssl.conf <<EOF +# Generated by: cache-conf $* + +# Enable caching +CacheEnable disk / +CacheQuickHandler Off +CacheRoot $root/cache +CacheHeader On +#CacheDetailHeader On +CacheIgnoreQueryString On +CacheIgnoreCacheControl On +CacheIgnoreHeaders Set-Cookie + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf new file mode 100755 index 0000000000..a30fdfff8c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf @@ -0,0 +1,74 @@ +#!/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 certificate-based authentication configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: cert-auth-conf $* +# Disallow public access to server resources + +EOF + +# Generate authentication configuration +cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: cert-auth-conf $* +# Require clients to present a valid client certificate +SSLVerifyClient require +SSLVerifyDepth 1 + +<Location /> +AuthType Basic +AuthName "$host" +AuthBasicProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +Require valid-user +</Location> + +EOF + +# Create password file and certificate-based users +cat >>$root/conf/httpd.passwd <<EOF +/C=US/ST=CA/L=San Francisco/O=$host/OU=server/CN=$host:xxj31ZMTZzkVA +/C=US/ST=CA/L=San Francisco/O=$host/OU=proxy/CN=$host:xxj31ZMTZzkVA +/C=US/ST=CA/L=San Francisco/O=$host/OU=tunnel/CN=$host:xxj31ZMTZzkVA +/C=US/ST=CA/L=San Francisco/O=localhost/OU=server/CN=localhost:xxj31ZMTZzkVA +/C=US/ST=CA/L=San Francisco/O=localhost/OU=tunnel/CN=localhost:xxj31ZMTZzkVA +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types new file mode 100644 index 0000000000..3f083f9a32 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types @@ -0,0 +1,608 @@ +# 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 b64 +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/cache-manifest cmf +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/lightweight-sca/modules/http/curl-connect.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp new file mode 100644 index 0000000000..9f5ee17368 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp @@ -0,0 +1,98 @@ +/* + * 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$ */ + +/** + * HTTP connect command line test tool. + */ + +#include <assert.h> +#include <stdio.h> +#include "stream.hpp" +#include "string.hpp" +#include "perf.hpp" +#include "http.hpp" + +namespace tuscany { +namespace http { + +const bool testConnect(const string& url, const string& ca = "", const string& cert = "", const string& key = "") { + gc_scoped_pool p; + + CURLSession cs(ca, cert, key, "", 0); + const failable<bool> crc = connect(url, cs); + assert(hasContent(crc)); + + apr_pollset_t* pollset; + apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0); + assert(cprc == APR_SUCCESS); + apr_socket_t* csock = sock(0, p); + const apr_pollfd_t* cpollfd = pollfd(csock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p); + apr_pollset_add(pollset, cpollfd); + apr_socket_t* tsock = sock(cs); + const apr_pollfd_t* tpollfd = pollfd(tsock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p); + apr_pollset_add(pollset, tpollfd); + + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + for(;;) { + apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds); + assert(pollrc == APR_SUCCESS); + + for (; pollcount > 0; pollcount--, pollfds++) { + if (pollfds->rtnevents & APR_POLLIN) { + char data[8192]; + if (pollfds->desc.s == csock) { + const size_t rl = ::read(0, data, sizeof(data)); + if (rl == (size_t)-1) + return false; + if (rl > 0) { + const failable<bool> src = http::send(data, rl, cs); + assert(hasContent(src)); + } + } + else { + const failable<size_t> frl = http::recv(data, sizeof(data), cs); + assert(hasContent(frl)); + const size_t rl = content(frl); + if (rl == 0) + return true; + const size_t wl = ::write(0, data, rl); + assert(wl == rl); + } + continue; + } + assert(!(pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL))); + } + } + return true; +} + +} +} + +int main(unused const int argc, const char** argv) { + if (argc > 2) + tuscany::http::testConnect(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4])); + else + tuscany::http::testConnect(tuscany::string(argv[1])); + return 0; +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp new file mode 100644 index 0000000000..a16daeeae3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTP GET command line test tool. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "perf.hpp" +#include "http.hpp" + +namespace tuscany { +namespace http { + +const bool testGet(const string& url, const string& ca = "", const string& cert = "", const string& key = "") { + CURLSession ch(ca, cert, key, "", 0); + const failable<value> val = get(url, ch); + assert(hasContent(val)); + cout << content(val) << endl; + return true; +} + +} +} + +int main(unused const int argc, const char** argv) { + if (argc > 2) + tuscany::http::testGet(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4])); + else + tuscany::http::testGet(tuscany::string(argv[1])); + return 0; +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp new file mode 100644 index 0000000000..f0806ea577 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "perf.hpp" +#include "http.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("", "", "", "", 0); + { + 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("", "", "", "", 0); + lambda<bool()> gl = getLoop(ch); + cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl; + return true; +} + +} +} + +int main() { + tuscany::gc_scoped_pool p; + 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/lightweight-sca/modules/http/form-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf new file mode 100755 index 0000000000..fbe943f3d9 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf @@ -0,0 +1,82 @@ +#!/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 form authentication configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then + pw=`cat $root/cert/ca.key | head -2 | tail -1` +else + pw="$3" +fi + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: form-auth-conf $* +# Disallow public access to server resources + +EOF + +# Generate form authentication configuration +cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: form-auth-conf $* +# Require clients to present a userid + password through form-based +# authentication +<Location /> +AuthType Form +AuthName "$host" +AuthFormProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +AuthFormLoginRequiredLocation /login/ +AuthFormLogoutLocation / +Session On +SessionCookieName TuscanyFormAuth domain=.$host; path=/ +SessionCryptoPassphrase $pw +Require valid-user +</Location> + +<Location /login/dologin> +SetHandler form-login-handler +</Location> + +<Location /logout/dologout> +SetHandler form-logout-handler +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf new file mode 100755 index 0000000000..e9617f696a --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf @@ -0,0 +1,57 @@ +#!/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. + +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` +user=$2 +group="members" + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: group-auth-conf $* +# Disallow public access to server resources + +EOF + +# Add user to group +cat $root/conf/httpd.groups | awk " BEGIN { found = 0 } /$group: / { printf \"%s %s\n\", \$0, \"$user\"; found = 1 } !/$group: / { printf \"%s\n\", \$0 } END { if (found == 0) printf \"%s: %s\n\", \"$group\", \"$user\" } " >$root/conf/.httpd.groups.tmp 2>/dev/null +cp $root/conf/.httpd.groups.tmp $root/conf/httpd.groups +rm $root/conf/.httpd.groups.tmp + +# Generate HTTPD group authorization configuration +conf=`cat $root/conf/locauth$sslsuffix.conf | grep "Generated by: group-auth-conf"` +if [ "$conf" = "" ]; then + cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: group-auth-conf $1 +# Allow group member access to root location +<Location /> +Require group members +</Location> + +EOF +fi + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html new file mode 100644 index 0000000000..236864edfb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html @@ -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. +--> + +<html> +<head> +<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/> +<meta name="apple-mobile-web-app-capable" content="yes"/> +<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> +<link rel="stylesheet" type="text/css" href="/ui-min.css"/> +<title>It works</title> +</head> +<body> +<h1>It works!</h1> +</body> +</html> + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html new file mode 100644 index 0000000000..fd3bc21889 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html @@ -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. +--> + +<html> +<head> +<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/> +<meta name="apple-mobile-web-app-capable" content="yes"/> +<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> +<link rel="stylesheet" type="text/css" href="/ui-min.css"/> +<script type="text/javascript" src="/all-min.js"></script> +<title>Sign in</title> +</head> +<body> +<h1>Sign in</h1> +<br/> + +<script type="text/javascript"> +function submitFormSignin() { + clearauthcookie(); + document.formSignin.httpd_location.value = '/'; + document.formSignin.submit(); +} +</script> + +<form name="formSignin" method="POST" action="/login/dologin"> +<table border="0"> +<tr><td>Username:</td><td><input type="text" name="httpd_username" value=""/></td></tr> +<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr> +<tr><td><input type="button" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr> +</table> +<input type="hidden" name="httpd_location" value="/"/> +</form> + +</body> +</html> diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html new file mode 100644 index 0000000000..218dd5d52c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html @@ -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. +--> + +<html> +<body> +<head> +<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/> +<meta name="apple-mobile-web-app-capable" content="yes"/> +<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> +<link rel="stylesheet" type="text/css" href="/ui-min.css"/> +<script type="text/javascript" src="/all-min.js"></script> +<title>Sign out</title> +</head> +<h1>Sign out</h1> +<br/> + +<form name="signout" action="/login" method="GET"> +<script type="text/javascript"> +function submitSignout() { + clearauthcookie(); + document.signout.submit(); + return true; +} +</script> +<input type="button" onclick="submitSignout()" value="Sign out"/> +</form> +</body> +</html> diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http-test b/sca-cpp/branches/lightweight-sca/modules/http/http-test new file mode 100755 index 0000000000..956b13a516 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/http-test @@ -0,0 +1,34 @@ +#!/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 +rm -rf tmp +./httpd-conf tmp localhost 8090 htdocs +./httpd-event-conf tmp +./httpd-start tmp +sleep 2 + +# Test +./curl-test 2>/dev/null +rc=$? + +# Cleanup +./httpd-stop tmp +sleep 2 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http.hpp b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp new file mode 100644 index 0000000000..408b9fdee5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp @@ -0,0 +1,1042 @@ +/* + * 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_http_hpp +#define tuscany_http_hpp + +/** + * CURL HTTP client functions. + */ + +#include <unistd.h> +#include <curl/curl.h> +#include <curl/easy.h> +#include <apr.h> +#include <apr_lib.h> +#include <apr_network_io.h> +#include <apr_portable.h> +#include <apr_poll.h> +#include <apr_uri.h> + +#include "string.hpp" +#include "gc.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "parallel.hpp" +#include "../scheme/io.hpp" +#include "../atom/atom.hpp" +#include "../rss/rss.hpp" +#include "../json/json.hpp" + +namespace tuscany { +namespace http { + +/** + * Enable CURL verbose debug log. + */ +//#define WANT_MAINTAINER_CURL_VERBOSE + +/** + * 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() : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(false), ca(""), cert(""), key(""), cookie(""), timeout(0) { + } + + CURLSession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(true), ca(ca), cert(cert), key(key), cookie(cookie), timeout(timeout) { + } + + CURLSession(const CURLSession& c) : h(c.h), p(c.p), sock(c.sock), wpollset(c.wpollset), wpollfd(c.wpollfd), rpollset(c.rpollset), rpollfd(c.rpollfd), owner(false), ca(c.ca), cert(c.cert), key(c.key), cookie(c.cookie), timeout(c.timeout) { + } + + const CURLSession& operator=(const CURLSession& c) { + if(this == &c) + return *this; + h = c.h; + p = c.p; + sock = c.sock; + wpollset = c.wpollset; + wpollfd = c.wpollfd; + rpollset = c.rpollset; + rpollfd = c.rpollfd; + owner = false; + ca = c.ca; + cert = c.cert; + key = c.key; + cookie = c.cookie; + timeout = c.timeout; + return *this; + } + + ~CURLSession() { + if (!owner) + return; + if (h == NULL) + return; + curl_easy_cleanup(h); + } + +private: + CURL* h; + gc_child_pool p; + apr_socket_t* sock; + apr_pollset_t* wpollset; + apr_pollfd_t* wpollfd; + apr_pollset_t* rpollset; + apr_pollfd_t* rpollfd; + bool owner; + + friend CURL* handle(const CURLSession& cs); + friend apr_socket_t* sock(const CURLSession& cs); + friend const failable<CURL*> setup(const string& url, CURLSession& cs); + friend const failable<bool> cleanup(CURLSession& cs); + friend const failable<bool> connect(const string& url, CURLSession& cs); + friend const failable<bool> send(const char* c, const size_t l, CURLSession& cs); + friend const failable<size_t> recv(char* c, const size_t l, CURLSession& cs); + +public: + string ca; + string cert; + string key; + string cookie; + int timeout; +}; + +/** + * Returns the CURL handle used by a CURL session. + */ +CURL* handle(const CURLSession& cs) { + return cs.h; +} + +/** + * Return an apr_socket_t for the socket used by a CURL session. + */ +apr_socket_t* sock(const CURLSession& cs) { + return cs.sock; +} + +/** + * Convert a socket fd to an apr_socket_t. + */ +apr_socket_t* sock(const int sd, const gc_pool& p) { + int fd = sd; + apr_socket_t* s = NULL; + apr_os_sock_put(&s, &fd, pool(p)); + return s; +} + +/** + * Convert a CURL return code to an error string. + */ +const string curlreason(CURLcode rc) { + return curl_easy_strerror(rc); +} + +/** + * Convert an APR status to an error string. + */ +const string apreason(apr_status_t rc) { + char buf[256]; + return apr_strerror(rc, buf, sizeof(buf)); +} + +/** + * Escape a URI or a query argument. + */ +const char escape_c2x[] = "0123456789ABCDEF"; + +const string escape(const string& unesc, const char* reserv) { + char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3); + const unsigned char* s = (const unsigned char *)c_str(unesc); + unsigned char* d = (unsigned char*)copy; + unsigned c; + while ((c = *s)) { + if (!apr_isalnum(c) && !strchr(reserv, c)) { + *d++ = '%'; + *d++ = escape_c2x[c >> 4]; + *d++ = escape_c2x[c & 0xf]; + } + else { + *d++ = (unsigned char)c; + } + ++s; + } + *d = '\0'; + return copy; +} + +const string escapeURI(const string& uri) { + debug(uri, "http::escapeURI::uri"); + const string e = escape(uri, "?$-_.+!*'(),:@&=/~%"); + debug(e, "http::escapeURI::result"); + return e; +} + +const string escapeArg(const string& arg) { + debug(arg, "http::escapeArg::arg"); + const string e = escape(arg, "-_.~"); + debug(e, "http::escapeArg::result"); + return e; +} + +/** + * Return true if a URI is absolute. + */ +const bool isAbsolute(const string& uri) { + return contains(uri, "://"); +} + +/** + * Parse a URI and return its host name. + */ +const string hostName(const string& uri, const gc_pool& p) { + apr_uri_t u; + const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u); + if (rc != APR_SUCCESS) + return ""; + if (u.hostname == NULL) + return ""; + return u.hostname; +} + +/** + * Parse a URI and return its scheme. + */ +const string scheme(const string& uri, const gc_pool& p) { + apr_uri_t u; + const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u); + if (rc != APR_SUCCESS) + return ""; + if (u.scheme == NULL) + return ""; + return u.scheme; +} + +/** + * Return the first subdomain name in a host name. + */ +const string subDomain(const string& host) { + return substr(host, 0, find(host, '.')); +} + +/** + * Return the top domain name in a host name. + */ +const string topDomain(const string& host) { + const size_t d = find(host, '.'); + return d == length(host) ? host : substr(host, d + 1); +} + +/** + * Setup a CURL session + */ +const failable<CURL*> setup(const string& url, CURLSession& cs) { + + // Init CURL session + if (cs.h != NULL) + cleanup(cs); + cs.h = curl_easy_init(); + CURL* ch = cs.h; + curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); +#ifdef WANT_MAINTAINER_CURL_VERBOSE + curl_easy_setopt(ch, CURLOPT_VERBOSE, true); +#endif + + // 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); + curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(ch, CURLOPT_TIMEOUT, cs.timeout); + + // Setup SSL options + if (cs.ca != "") { + debug(cs.ca, "http::setup::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); + } else + curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, false); + if (cs.cert != "") { + debug(cs.cert, "http::setup::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::setup::key"); + curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key)); + curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); + } + if (cs.cookie != "") { + debug(cs.cookie, "http::setup::cookie"); + curl_easy_setopt(ch, CURLOPT_COOKIE, c_str(cs.cookie)); + } + + // Set up HTTP basic auth if requested + apr_uri_t u; + apr_pool_t* p = gc_current_pool(); + const apr_status_t prc = apr_uri_parse(p, c_str(url), &u); + if (prc == APR_SUCCESS) { + if (u.user != NULL) { + debug(u.user, "http::setup::user"); + curl_easy_setopt(ch, CURLOPT_USERNAME, u.user); + } + if (u.password != NULL) { + debug(u.password, "http::setup::pass"); + curl_easy_setopt(ch, CURLOPT_PASSWORD, u.password); + } + if (u.user != NULL || u.password != NULL) { + curl_easy_setopt(ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + + // Set target URL, omitting the user:password part + curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(apr_uri_unparse(p, &u, APR_URI_UNP_OMITUSERINFO)))); + + return ch; + } + } + + // Set target URL + curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(url))); + + return ch; +} + +/** + * Cleanup a CURL session + */ +const failable<bool> cleanup(CURLSession& cs) { + if (cs.h == NULL) + return true; + curl_easy_cleanup(cs.h); + cs.h = NULL; + return true; +} + +/** + * 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, CURLSession& cs) { + debug(url, "http::apply::url"); + debug(verb, "http::apply::verb"); + + // Setup the CURL session + const failable<CURL*> fch = setup(url, cs); + if (!hasContent(fch)) { + cleanup(cs); + return mkfailure<list<R>>(fch); + } + CURL* ch = content(fch); + + // Set the request headers + curl_slist* hl = headers(NULL, car(hdr)); + if (hl != NULL) + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + + // Convert request body to a string + // TODO use HTTP chunking instead + ostringstream os; + write(cadr(hdr), os); + const string s = str(os); + const size_t sz = length(s); + + // Setup the read, write header and write data 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); + + // Apply the HTTP verb + 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 == "PATCH") { + curl_easy_setopt(ch, CURLOPT_UPLOAD, true); + curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "PATCH"); + 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); + + // Free the headers + if (hl != NULL) + curl_slist_free_all(hl); + + // Return the HTTP return code or content + if (rc) { + cleanup(cs); + return mkfailure<list<R> >(string(curl_easy_strerror(rc))); + } + long httprc; + curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); + if (httprc != 200 && httprc != 201) { + cleanup(cs); + ostringstream es; + es << "HTTP code " << httprc; + return mkfailure<list<R> >(str(es)); + } + + cleanup(cs); + 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, CURLSession& cs) { + debug(url, "http::evalExpr::url"); + debug(expr, "http::evalExpr::input"); + + // Convert expression to a JSON-RPC request + js::JSContext cx; + const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx); + if (!hasContent(jsreq)) + return mkfailure<value>(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", cs); + if (!hasContent(res)) + return mkfailure<value>(res); + + // Parse and return JSON-RPC result + const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx); + debug(rval, "http::evalExpr::result"); + if (!hasContent(rval)) + return mkfailure<value>(rval); + return content(rval); +} + +/** + * Find and return a header. + */ +const maybe<string> header(const char* prefix, const list<string>& h) { + if (isNil(h)) + return maybe<string>(); + 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 string location(const list<string>& h) { + const maybe<string> l = header("Location: ", h); + return hasContent(l)? content(l) : ""; +} + +/** + * Convert a location to an entry id. + */ +const value entryId(const failable<string> l) { + if (!hasContent(l)) + return list<value>(); + const string ls(content(l)); + return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1)))); +} + +/** + * Find and return a content-type header. + */ +const string contentType(const list<string>& h) { + const maybe<string> ct = header("Content-Type: ", h); + return hasContent(ct)? content(ct) : ""; +} + +/** + * 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, CURLSession& cs) { + debug(url, "http::get::url"); + const list<list<string> > req = mklist(list<string>(), list<string>()); + return apply(req, reduce, initial, url, "GET", cs); +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value> getcontent(const string& url, CURLSession& cs) { + 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, cs); + if (!hasContent(res)) + return mkfailure<value>(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; +} + +/** + * Convert an HTTP content response to a value. + */ +const failable<value> responseValue(const list<list<string> > res) { + + // Parse the returned content + const string ct = contentType(car(res)); + debug(ct, "http::responseValue::contentType"); + + const list<string> ls(reverse(cadr(res))); + debug(ls, "http::responseValue::content"); + + if (atom::isATOMEntry(ls)) { + // Read an ATOM entry + const value val(elementsToValues(content(atom::readATOMEntry(ls)))); + debug(val, "http::responseValue::result"); + return val; + } + if (contains(ct, "application/atom+xml") || atom::isATOMFeed(ls)) { + // Read an ATOM feed + const value val(elementsToValues(content(atom::readATOMFeed(ls)))); + debug(val, "http::responseValue::result"); + return val; + } + if (contains(ct, "application/rss+xml") || rss::isRSSFeed(ls)) { + // Read an RSS feed + const value val(elementsToValues(content(rss::readRSSFeed(ls)))); + debug(val, "http::responseValue::result"); + return val; + } + if (contains(ct, "text/javascript") || contains(ct, "application/json") || json::isJSON(ls)) { + // Read a JSON document + js::JSContext cx; + const value val(json::jsonValues(content(json::readJSON(ls, cx)))); + debug(val, "http::responseValue::result"); + return val; + } + if (contains(ct, "application/x-javascript")) { + // Read a JSON document enclosed in a javascript function call + // Extract the JSON out of the enclosing parenthesis + ostringstream os; + write(ls, os); + const string s = str(os); + const size_t fp = find(s, '('); + const size_t lp = find_last(s, ')'); + const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1))); + debug(jls, "http::responseValue::javascript::content"); + + js::JSContext cx; + const value val(json::jsonValues(content(json::readJSON(jls, cx)))); + debug(val, "http::responseValue::result"); + return val; + } + if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) { + // Read an XML document + const value val(elementsToValues(readXML(ls))); + debug(val, "http::responseValue::result"); + return val; + } + + // Return the content type and a content list + const value val(mklist<value>(ct, mkvalues(ls))); + debug(val, "http::responseValue::result"); + return val; +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value> get(const string& url, CURLSession& cs) { + 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, cs); + if (!hasContent(res)) + return mkfailure<value>(res); + + // Parse the returned content + return responseValue(content(res)); +} + +/** + * Form an HTTP content request. + */ +const failable<list<list<string> > > writeRequest(const failable<list<string> >& ls, const string& ct) { + if (!hasContent(ls)) + return mkfailure<list<list<string> > >(ls); + const list<list<string> > req = mklist<list<string> >(mklist<string>(string("Content-Type: ") + ct), content(ls)); + debug(req, "http::writeRequest::req"); + return req; +} + +/** + * Convert a value to an HTTP content request. + */ +const failable<list<list<string> > > contentRequest(const value& c, unused const string& url) { + + // Check if the client requested a specific format + //TODO derive that from given URL + const list<value> fmt = assoc<value>("format", list<value>()); + + // Write as a scheme value if requested by the client + if (!isNil(fmt) && cadr(fmt) == "scheme") + return writeRequest(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8"); + + // Write a simple value as a JSON value + if (!isList(c)) { + js::JSContext cx; + if (isSymbol(c)) { + const list<value> lc = mklist<value>(mklist<value>("name", value(string(c)))); + debug(lc, "http::contentRequest::symbol"); + return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); + } + const list<value> lc = mklist<value>(mklist<value>("value", c)); + debug(lc, "http::contentRequest::value"); + return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); + } + + // Write an empty list as a JSON empty value + if (isNil((list<value>)c)) { + js::JSContext cx; + debug(list<value>(), "http::contentRequest::empty"); + return writeRequest(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8"); + } + + // Write content-type / content-list pair + if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c))) + return writeRequest(convertValues<string>(cadr<value>(c)), car<value>(c)); + + // Write an assoc value as JSON + if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) { + js::JSContext cx; + const list<value> lc = mklist<value>(c); + debug(lc, "http::contentRequest::assoc"); + debug(valuesToElements(lc), "http::contentRequest::assoc::element"); + return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8"); + } + + // Write value as JSON if requested by the client + if (!isNil(fmt) && cadr(fmt) == "json") { + js::JSContext cx; + return writeRequest(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8"); + } + + // Convert list of values to element values + const list<value> e = valuesToElements(c); + debug(e, "http::contentRequest::elements"); + + // Write an ATOM feed or entry + if (isList(car<value>(e)) && !isNil(car<value>(e))) { + const list<value> el = car<value>(e); + if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) { + if (cadr<value>(el) == atom::feed) + return writeRequest(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8"); + if (cadr<value>(el) == atom::entry) + return writeRequest(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8"); + } + } + + // Write any other compound value as a JSON value + js::JSContext cx; + return writeRequest(json::writeJSON(e, cx), "application/json; charset=utf-8"); +} + +/** + * HTTP POST. + */ +const failable<value> post(const value& val, const string& url, CURLSession& cs) { + debug(url, "http::post::url"); + + // Convert value to a content request + const failable<list<list<string> > > req = contentRequest(val, url); + if (!hasContent(req)) + return mkfailure<value>(req); + debug(content(req), "http::post::input"); + + // POST it to the URL + const failable<list<list<string> > > res = apply<list<string>>(content(req), rcons<string>, list<string>(), url, "POST", cs); + if (!hasContent(res)) + return mkfailure<value>(res); + + // Return the new entry id from the HTTP location header, if any + const string loc = location(car(content(res))); + if (length(loc) != 0) { + const value eid(entryId(location(car(content(res))))); + debug(eid, "http::post::result"); + return eid; + } + + // Return the returned content + return responseValue(content(res)); +} + +/** + * HTTP PUT. + */ +const failable<value> put(const value& val, const string& url, CURLSession& cs) { + debug(url, "http::put::url"); + + // Convert value to a content request + const failable<list<list<string> > > req = contentRequest(val, url); + if (!hasContent(req)) + return mkfailure<value>(req); + debug(content(req), "http::put::input"); + + // PUT it to the URL + const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PUT", cs); + if (!hasContent(res)) + return mkfailure<value>(res); + + debug(true, "http::put::result"); + return value(true); +} + +/** + * HTTP PATCH. + */ +const failable<value> patch(const value& val, const string& url, CURLSession& cs) { + debug(url, "http::put::patch"); + + // Convert value to a content request + const failable<list<list<string> > > req = contentRequest(val, url); + if (!hasContent(req)) + return mkfailure<value>(req); + debug(content(req), "http::patch::input"); + + // PATCH it to the URL + const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PATCH", cs); + if (!hasContent(res)) + return mkfailure<value>(res); + + debug(true, "http::patch::result"); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable<value, string> del(const string& url, CURLSession& cs) { + 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", cs); + if (!hasContent(res)) + return mkfailure<value>(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; +} + +/** + * Create an APR pollfd for a socket. + */ +apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) { + apr_pollfd_t* pfd = gc_new<apr_pollfd_t>(p); + pfd->p = pool(p); + pfd->desc_type = APR_POLL_SOCKET; + pfd->reqevents = (apr_int16_t)e; + pfd->rtnevents = (apr_int16_t)e; + pfd->desc.s = s; + pfd->client_data = NULL; + return pfd; +} + +/** + * Connect to a URL. + */ +const failable<bool> connect(const string& url, CURLSession& cs) { + debug(url, "http::connect::url"); + + // Setup the CURL session + const failable<CURL*> fch = setup(url, cs); + if (!hasContent(fch)) { + cleanup(cs); + return mkfailure<bool>(fch); + } + CURL* ch = content(fch); + + // Connect + curl_easy_setopt(ch, CURLOPT_CONNECT_ONLY, true); + const CURLcode rc = curl_easy_perform(ch); + if (rc) { + cleanup(cs); + return mkfailure<bool>(string(curl_easy_strerror(rc))); + } + + // Convert the connected socket to an apr_socket_t + int sd; + const CURLcode grc = curl_easy_getinfo(ch, CURLINFO_LASTSOCKET, &sd); + if (grc) { + cleanup(cs); + return mkfailure<bool>(string(curl_easy_strerror(grc))); + } + cs.sock = sock(sd, cs.p); + + // Create pollsets and pollfds which can be used to poll the socket + apr_status_t rpcrc = apr_pollset_create(&cs.rpollset, 1, pool(cs.p), 0); + if (rpcrc != APR_SUCCESS) { + cleanup(cs); + return mkfailure<bool>(apreason(rpcrc)); + } + cs.rpollfd = pollfd(cs.sock, APR_POLLIN, cs.p); + apr_pollset_add(cs.rpollset, cs.rpollfd); + apr_status_t wpcrc = apr_pollset_create(&cs.wpollset, 1, pool(cs.p), 0); + if (wpcrc != APR_SUCCESS) { + cleanup(cs); + return mkfailure<bool>(apreason(wpcrc)); + } + cs.wpollfd = pollfd(cs.sock, APR_POLLOUT, cs.p); + apr_pollset_add(cs.wpollset, cs.wpollfd); + + return true; +} + +/** + * Send an array of chars. + */ +const failable<bool> send(const char* c, const size_t l, CURLSession& cs) { + + // Send the data + size_t wl = 0; + const CURLcode rc = curl_easy_send(cs.h, c, (size_t)l, &wl); + if (rc == CURLE_OK && wl == (size_t)l) + return true; + if (rc != CURLE_AGAIN) { + cleanup(cs); + return mkfailure<bool>(curlreason(rc)); + } + + // If the socket was not ready, wait for it to become ready + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + apr_status_t pollrc = apr_pollset_poll(cs.wpollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) + return mkfailure<bool>(apreason(pollrc)); + + // Send what's left + return send(c + wl, l - wl, cs); +} + +/** + * Receive an array of chars. + */ +const failable<size_t> recv(char* c, const size_t l, CURLSession& cs) { + + // Receive data + size_t rl; + const CURLcode rc = curl_easy_recv(cs.h, c, (size_t)l, &rl); + if (rc == CURLE_OK) + return (size_t)rl; + if (rc == 1) + return 0; + if (rc != CURLE_AGAIN) { + cleanup(cs); + return mkfailure<size_t>(curlreason(rc)); + } + + // If the socket was not ready, wait for it to become ready + const apr_pollfd_t* pollfds; + apr_int32_t pollcount; + apr_status_t pollrc = apr_pollset_poll(cs.rpollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) { + cleanup(cs); + return mkfailure<size_t>(apreason(pollrc)); + } + + // Receive again + return recv(c, l, cs); +} + +/** + * Converts a list of key value pairs to a query string. + */ +ostringstream& queryString(const list<list<value> > args, ostringstream& os) { + if (isNil(args)) + return os; + const list<value> arg = car(args); + debug(arg, "http::queryString::arg"); + if (isNil(arg) || isNil(cdr(arg))) + return queryString(cdr(args), os); + os << car(arg) << "=" << c_str(cadr(arg)); + if (!isNil(cdr(args))) + os << "&"; + return queryString(cdr(args), os); +} + +const string queryString(const list<list<value> > args) { + ostringstream os; + return str(queryString(args, os)); +} + +/** + * Filter path segment in a list of arguments. + */ +const bool filterPath(const value& arg) { + return isString(arg) || isSymbol(arg); +} + +/** + * Filter query string arguments in a list of arguments. + */ +const bool filterQuery(const value& arg) { + return isList(arg); +} + +/** + * Escape a query string argument. + */ +const value escapeQuery(const value& arg) { + return arg; + //return mklist<value>(car<value>(arg), escapeArg(cadr<value>(arg))); +} + +/** + * HTTP client proxy function. + */ +struct proxy { + proxy(const string& uri, const string& ca, const string& cert, const string& key, const string& cookie, const int timeout, const gc_pool& p) : p(p), uri(uri), ca(ca), cert(cert), key(key), cookie(cookie), cs(*(new (gc_new<CURLSession>(p)) CURLSession(ca, cert, key, cookie, timeout))) { + } + + const value operator()(const list<value>& args) const { + debug(args, "http::proxy::args"); + const value fun = car(args); + if (fun == "get") { + const list<value> lp = filter<value>(filterPath, cadr(args)); + debug(lp, "http::proxy::path"); + const list<value> lq = map<value, value>(escapeQuery, filter<value>(filterQuery, cadr(args))); + debug(lp, "http::proxy::query"); + const value p = path(lp); + const value q = queryString(lq); + const failable<value> val = get(uri + p + (q != ""? string("?") + q : string("")), cs); + return content(val); + } + if (fun == "post") { + const failable<value> val = post(caddr(args), uri + path(cadr(args)), cs); + return content(val); + } + if (fun == "put") { + const failable<value> val = put(caddr(args), uri + path(cadr(args)), cs); + return content(val); + } + if (fun == "patch") { + const failable<value> val = patch(caddr(args), uri + path(cadr(args)), cs); + return content(val); + } + if (fun == "delete") { + const failable<value> val = del(uri + path(cadr(args)), cs); + return content(val); + } + const failable<value> val = evalExpr(args, uri, cs); + return content(val); + } + + const gc_pool p; + const string uri; + const string ca; + const string cert; + const string key; + const string cookie; + CURLSession& cs; +}; + +} +} + +#endif /* tuscany_http_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr new file mode 100755 index 0000000000..735c152f43 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr @@ -0,0 +1,53 @@ +#!/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. + +# Parse a string in the form ip-addr:local-port/public-port +addr=`echo $2 | awk -F "/" '{ print $1 }'` +ip=`echo $addr | awk -F ":" '{ print $1 }'` +port=`echo $addr | awk -F ":" '{ print $2 }'` +if [ "$port" = "" ]; then + port=$ip + ip="" + listen=$port + vhost="*:$port" +else + listen="$ip:$port" + vhost="$ip:$port" +fi +pport=`echo $2 | awk -F "/" '{ print $2 }'` +if [ "$pport" = "" ]; then + pport=$port +fi + +# Return the requested part +if [ "$1" = "ip" ]; then + echo $ip +fi +if [ "$1" = "port" ]; then + echo $port +fi +if [ "$1" = "pport" ]; then + echo $pport +fi +if [ "$1" = "listen" ]; then + echo $listen +fi +if [ "$1" = "vhost" ]; then + echo $vhost +fi diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf new file mode 100755 index 0000000000..730775fa89 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf @@ -0,0 +1,375 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 +port=`$here/httpd-addr port $3` +pport=`$here/httpd-addr pport $3` +listen=`$here/httpd-addr listen $3` +vhost=`$here/httpd-addr vhost $3` +if [ "$pport" = "80" ]; then + pportsuffix="" +else + pportsuffix=":$pport" +fi + +mkdir -p $4 +htdocs=`echo "import os; print os.path.realpath('$4')" | python` + +user=`id -un` +group=`id -gn` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" + sendfile=Off +else + libsuffix=".so" + sendfile=On +fi + +modules_prefix=`cat $here/httpd-modules.prefix` + +mkdir -p $root +mkdir -p $root/logs +mkdir -p $root/conf +cat >$root/conf/httpd.conf <<EOF +# Generated by: httpd-conf $* +# Apache HTTPD server configuration + +# Main server name +ServerName http://$host$pportsuffix +PidFile $root/logs/httpd.pid + +# Load configured MPM +Include conf/mpm.conf + +# Load required modules +Include conf/modules.conf + +# Basic security precautions +User $user +Group $group +ServerSignature Off +ServerTokens Prod +Timeout 45 +RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500 +LimitRequestBody 1048576 +HostNameLookups Off +#MaxKeepAliveRequests 25 +#MaxConnectionsPerChild 100 + +# Log HTTP requests +# [timestamp] [access] remote-host remote-ident remote-user "request-line" +# status response-size "referrer" "user-agent" "user-track" local-IP +# virtual-host response-time bytes-received bytes-sent +LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [access] %h %l %u \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" combined +Include conf/log.conf + +# Configure Mime types and default charsets +TypesConfig $here/conf/mime.types +AddDefaultCharset utf-8 +AddCharset utf-8 .html .js .css + +# Configure cache control +<Directory /> +ExpiresActive On +ExpiresDefault A604800 +Header onsuccess merge Cache-Control public env=!private-cache +</Directory> + +# Enable Linux Kernel sendfile +EnableSendFile $sendfile + +# Configure auth modules +Include conf/auth.conf + +# Set default document root +DocumentRoot $htdocs +DirectoryIndex index-min.html index.html + +# Protect server files +<Directory /> +Options None +AllowOverride None +Require all denied +</Directory> + +# Configure output filters to enable compression and rate limiting +<Location /> +#SetOutputFilter RATE_LIMIT;DEFLATE +SetOutputFilter DEFLATE + +BrowserMatch ^Mozilla/4 gzip-only-text/html +BrowserMatch ^Mozilla/4\.0[678] no-gzip +BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html +BrowserMatch ^check_http/ check_http +SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary +Header append Vary User-Agent env=!dont-vary + +#SetEnv rate-limit 400 +</Location> + +# Listen on HTTP port +Listen $listen + +# Setup HTTP virtual host +<VirtualHost $vhost> +ServerName http://$host$pportsuffix + +<Location /> +RewriteEngine on +Include conf/hostcond.conf +RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC] +RewriteCond %{REQUEST_URI} !^/server-status [NC] +RewriteCond %{REQUEST_URI} !^/balancer-manager [NC] +RewriteCond %{REQUEST_URI} !^/proxy/ [NC] +RewriteRule .* http://$host$pportsuffix%{REQUEST_URI} [R] +</Location> + +Include conf/svhost.conf + +# Configure authentication +Include conf/noauth.conf +Include conf/locauth.conf +Include conf/pubauth.conf +Include conf/adminauth.conf + +</VirtualHost> + +EOF + +# Configure logging +cat >$root/conf/log.conf <<EOF +# Generated by: httpd-conf $* +ErrorLog $root/logs/error_log +CustomLog $root/logs/access_log combined + +EOF + +# Run with the prefork MPM +cat >$root/conf/mpm.conf <<EOF +# Generated by: httpd-conf $* +LoadModule mpm_prefork_module ${modules_prefix}/modules/mod_mpm_prefork.so + +EOF + +if [ $uname = "Darwin" ]; then + cat >>$root/conf/mpm.conf <<EOF +# Generated by: httpd-conf $* +# Set thread stack size +ThreadStackSize 2097152 + +EOF +fi + +# Generate modules list +cat >$root/conf/modules.conf <<EOF +# Generated by: httpd-conf $* +# Load a minimal set of modules, the load order is important +# (e.g. load mod_headers before mod_rewrite, so its hooks execute +# after mod_rewrite's hooks) +LoadModule headers_module ${modules_prefix}/modules/mod_headers.so +LoadModule alias_module ${modules_prefix}/modules/mod_alias.so +LoadModule authn_file_module ${modules_prefix}/modules/mod_authn_file.so +LoadModule authn_socache_module ${modules_prefix}/modules/mod_authn_socache.so +LoadModule authn_core_module ${modules_prefix}/modules/mod_authn_core.so +LoadModule authz_host_module ${modules_prefix}/modules/mod_authz_host.so +LoadModule authz_groupfile_module ${modules_prefix}/modules/mod_authz_groupfile.so +LoadModule authz_user_module ${modules_prefix}/modules/mod_authz_user.so +LoadModule authz_core_module ${modules_prefix}/modules/mod_authz_core.so +LoadModule auth_basic_module ${modules_prefix}/modules/mod_auth_basic.so +LoadModule auth_digest_module ${modules_prefix}/modules/mod_auth_digest.so +LoadModule auth_form_module ${modules_prefix}/modules/mod_auth_form.so +LoadModule request_module ${modules_prefix}/modules/mod_request.so +LoadModule deflate_module ${modules_prefix}/modules/mod_deflate.so +LoadModule filter_module ${modules_prefix}/modules/mod_filter.so +LoadModule proxy_module ${modules_prefix}/modules/mod_proxy.so +LoadModule proxy_connect_module ${modules_prefix}/modules/mod_proxy_connect.so +LoadModule proxy_http_module ${modules_prefix}/modules/mod_proxy_http.so +LoadModule proxy_balancer_module ${modules_prefix}/modules/mod_proxy_balancer.so +LoadModule lbmethod_byrequests_module ${modules_prefix}/modules/mod_lbmethod_byrequests.so +LoadModule socache_shmcb_module ${modules_prefix}/modules/mod_socache_shmcb.so +LoadModule cache_module ${modules_prefix}/modules/mod_cache.so +LoadModule cache_disk_module ${modules_prefix}/modules/mod_cache_disk.so +LoadModule rewrite_module ${modules_prefix}/modules/mod_rewrite.so +LoadModule mime_module ${modules_prefix}/modules/mod_mime.so +LoadModule status_module ${modules_prefix}/modules/mod_status.so +LoadModule negotiation_module ${modules_prefix}/modules/mod_negotiation.so +LoadModule dir_module ${modules_prefix}/modules/mod_dir.so +LoadModule setenvif_module ${modules_prefix}/modules/mod_setenvif.so +LoadModule env_module ${modules_prefix}/modules/mod_env.so +LoadModule expires_module ${modules_prefix}/modules/mod_expires.so +<IfModule !log_config_module> +LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so +</IfModule> +LoadModule logio_module ${modules_prefix}/modules/mod_logio.so +LoadModule usertrack_module ${modules_prefix}/modules/mod_usertrack.so +LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so +LoadModule cgi_module ${modules_prefix}/modules/mod_cgi.so +LoadModule actions_module ${modules_prefix}/modules/mod_actions.so +LoadModule unixd_module ${modules_prefix}/modules/mod_unixd.so +LoadModule session_module ${modules_prefix}/modules/mod_session.so +LoadModule session_crypto_module ${modules_prefix}/modules/mod_session_crypto.so +LoadModule slotmem_shm_module ${modules_prefix}/modules/mod_slotmem_shm.so +LoadModule ratelimit_module ${modules_prefix}/modules/mod_ratelimit.so +LoadModule reqtimeout_module ${modules_prefix}/modules/mod_reqtimeout.so +LoadModule ssl_module ${modules_prefix}/modules/mod_ssl.so + +EOF + +# Generate auth configuration +cat >$root/conf/auth.conf <<EOF +# Generated by: httpd-conf $* + +EOF + +cat >$root/conf/locauth.conf <<EOF +# Generated by: httpd-conf $* +# Authentication and authorization configuration + +# Allow authorized access to document root +<Directory "$htdocs"> +Options FollowSymLinks +Require all granted +</Directory> + +# Allow authorized access to root location +<Location /> +Options FollowSymLinks +AuthUserFile "$root/conf/httpd.passwd" +AuthGroupFile "$root/conf/httpd.groups" +Require all granted +</Location> + +EOF + +cat >$root/conf/pubauth.conf <<EOF +# Generated by: httpd-conf $* +# Allow everyone to access public locations +<Location /login> +AuthType None +Require all granted +# Mark login page with a header +Header set X-Login open-auth +</Location> +<Location /logout> +AuthType None +Require all granted +</Location> +<Location /public> +AuthType None +Require all granted +</Location> +<Location /proxy/public> +AuthType None +Require all granted +</Location> +<Location /favicon.ico> +AuthType None +Require all granted +</Location> +<Location /robots.txt> +AuthType None +Require all granted +</Location> + +EOF + +cat >$root/conf/adminauth.conf <<EOF + +# Allow the server admin to view the server status +<Location /server-status> +Require user admin +</Location> + +EOF + +# Create password and group files +cat >$root/conf/httpd.passwd <<EOF +# Generated by: httpd-conf $* +EOF + +cat >$root/conf/httpd.groups <<EOF +# Generated by: httpd-conf $* +EOF + +# Allow public access to server resources +cat >$root/conf/noauth.conf <<EOF +# Generated by: httpd-conf $* +# Allow public access to server resources + +# Allow access to document root +<Directory "$htdocs"> +AuthType None +Require all granted +</Directory> + +# Allow everyone to access root location +<Location /> +AuthType None +Require all granted +</Location> + +EOF + +# Generate vhost configuration +cat >$root/conf/vhost.conf <<EOF +# Generated by: httpd-conf $* +# Virtual host configuration +UseCanonicalName Off + +# Enable HTTP reverse proxy +ProxyRequests Off +ProxyPreserveHost On +ProxyStatus On + +# Enable server status +<Location /server-status> +SetHandler server-status +HostnameLookups on +</Location> + +EOF + +cat >$root/conf/svhost.conf <<EOF +# Generated by: httpd-conf $* +# Static virtual host configuration +Include conf/vhost.conf + +EOF + +cat >$root/conf/dvhost.conf <<EOF +# Generated by: httpd-conf $* +# Mass dynamic virtual host configuration +Include conf/vhost.conf + +EOF + +# Generate host name check condition +cat >$root/conf/hostcond.conf <<EOF +# Generated by: httpd-conf $* +RewriteCond %{HTTP_HOST} !^$host [NC] + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug new file mode 100755 index 0000000000..df0d21fbe5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug @@ -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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +httpd=`cat $here/httpd.prefix` +$httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf new file mode 100755 index 0000000000..093bbe78d9 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf @@ -0,0 +1,45 @@ +#!/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. + +# Configure HTTPD to run with the event MPM +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +modules_prefix=`cat $here/httpd-modules.prefix` + +mkdir -p $root +mkdir -p $root/conf +cat >$root/conf/mpm.conf <<EOF +# Generated by: httpd-event-conf $* +# Use HTTPD event MPM +LoadModule mpm_event_module ${modules_prefix}/modules/mod_mpm_event.so + +EOF + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + cat >>$root/conf/mpm.conf <<EOF +# Generated by: httpd-conf $* +# Set thread stack size +ThreadStackSize 2097152 + +EOF +fi + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf new file mode 100755 index 0000000000..c9d2ad81d5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf @@ -0,0 +1,32 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Configure HTTPD log level +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` +level=$2 + +# Configure log level +cat >>$root/conf/log.conf <<EOF +# Generated by: httpd-loglevel-conf $* +LogLevel $level + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind new file mode 100755 index 0000000000..55e113bbd3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind @@ -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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +httpd=`cat $here/httpd.prefix` +$here/../../etc/memgrind $httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart b/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart new file mode 100755 index 0000000000..81b098d85d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +apachectl=`cat $here/httpd-apachectl.prefix` +$apachectl -k graceful -d $root -f $root/conf/httpd.conf diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf new file mode 100755 index 0000000000..f99a10071c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf @@ -0,0 +1,259 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` +gport=`echo $conf | awk '{ print $7 }'` +port=`$here/httpd-addr port $gport` +pport=`$here/httpd-addr pport $gport` + +sslpport=`$here/httpd-addr pport $2` +sslport=`$here/httpd-addr listen $2` +sslvhost=`$here/httpd-addr vhost $2` +if [ "$sslpport" = "443" ]; then + sslpportsuffix="" +else + sslpportsuffix=":$sslpport" +fi + +dothost=`echo $host | grep "\."` + +htdocs=`echo $conf | awk '{ print $8 }'` +mkdir -p $htdocs +htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +modules_prefix=`cat $here/httpd-modules.prefix` + +# Extract organization name from our CA certificate +org=`openssl x509 -noout -subject -nameopt multiline -in $root/cert/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'` + +# Generate HTTPD configuration +cat >>$root/conf/httpd.conf <<EOF +# Generated by: httpd-ssl-conf $* + +# Configure 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 +Mutex "file:$root/logs" ssl-cache +SSLRandomSeed startup builtin +SSLRandomSeed connect builtin + +# Listen on HTTPS port +Listen $sslport + +# HTTPS virtual host +<VirtualHost $sslvhost> +ServerName https://$host$sslpportsuffix + +<Location /> +RewriteEngine on +Include conf/hostcond.conf +RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC] +RewriteCond %{REQUEST_URI} !^/server-status [NC] +RewriteCond %{REQUEST_URI} !^/balancer-manager [NC] +RewriteCond %{REQUEST_URI} !^/proxy/ [NC] +RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R] +</Location> + +Include conf/svhost-ssl.conf + +# Configure authentication +Include conf/noauth-ssl.conf +Include conf/locauth-ssl.conf +Include conf/pubauth-ssl.conf +Include conf/adminauth-ssl.conf + +# Configure tracking +Include conf/tracking-ssl.conf + +</VirtualHost> + +EOF + +# Generate auth configuration +cat >$root/conf/locauth-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Authentication and authorization configuration +Include conf/locauth.conf + +EOF + +cat >$root/conf/pubauth-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Allow everyone to access public locations +Include conf/pubauth.conf + +EOF + +cat >$root/conf/adminauth-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Allow admin access +Include conf/adminauth.conf + +EOF + +# Allow public access to server resources +cat >$root/conf/noauth-ssl.conf <<EOF +# Generated by: httpd-conf $* +# Allow public access to server resources +Include conf/noauth.conf + +EOF + +# Generate HTTP vhost configuration +cat >>$root/conf/svhost.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Redirect HTTP traffic to HTTPS +<Location /> +RewriteEngine on +RewriteCond %{SERVER_PORT} ^$port$ [OR] +RewriteCond %{SERVER_PORT} ^$pport$ +RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R] +</Location> + +EOF + +# Redirect HTTP traffic to HTTPS in HTTP vhost +cat >>$root/conf/dvhost.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Redirect HTTP traffic to HTTPS +<Location /> +RewriteEngine on +RewriteCond %{SERVER_PORT} ^$port$ [OR] +RewriteCond %{SERVER_PORT} ^$pport$ +RewriteRule .* https://%{SERVER_NAME}$sslpportsuffix%{REQUEST_URI} [R] +</Location> + +EOF + +# Generate HTTPS vhost configuration +cat >$root/conf/vhost-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Virtual host configuration +UseCanonicalName Off + +# Enable SSL +SSLEngine on +SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL +BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0 +SSLOptions +StrictRequire +OptRenegotiate +FakeBasicAuth + +# Require clients to use SSL and authenticate +<Location /> +SSLRequireSSL +SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 +</Location> + +# Log SSL requests +# [timestamp] [sslaccess] remote-host remote-ident remote-user SSL-protocol +# SSL-cipher "request-line" status response-size "referrer" "user-agent" +# "SSL-client-I-DN" "SSL-client-S-DN" "user-track" local-IP virtual-host +# response-time bytes-received bytes-sent +LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [sslaccess] %h %l %u %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{SSL_CLIENT_I_DN}x\" \"%{SSL_CLIENT_S_DN}x\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" sslcombined +Include conf/log-ssl.conf + +# Enable HTTPS reverse proxy +ProxyRequests Off +ProxyPreserveHost On +ProxyStatus On +SSLProxyEngine on +SSLProxyCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL + +# Verify server certificates +SSLProxyVerify require +SSLProxyVerifyDepth 1 +SSLProxyCheckPeerCN Off + +# Enable server status +<Location /server-status> +SetHandler server-status +HostnameLookups on +</Location> + +EOF + +# Generate tracking configuration +cat >$root/conf/tracking-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Configure tracking +CookieTracking on +CookieName TuscanyVisitorId +CookieStyle Cookie +CookieExpires 31556926 + +EOF + +if [ "$dothost" != "" ]; then + cat >>$root/conf/tracking-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +CookieDomain .$dothost + +EOF + +fi + +# Configure logging +cat >$root/conf/log-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +CustomLog $root/logs/ssl_access_log sslcombined + +EOF + +# Configure virtual hosts +cat >$root/conf/svhost-ssl.conf <<EOF +# Generated by: httpd-ssl-conf $* +# Static virtual host configuration +Include conf/vhost-ssl.conf + +# Declare SSL certificates used in this virtual host +SSLCACertificateFile "$root/cert/ca.crt" +SSLCertificateChainFile "$root/cert/ca.crt" +SSLCertificateFile "$root/cert/server.crt" +SSLCertificateKeyFile "$root/cert/server.key" + +EOF + +cat >$root/conf/dvhost-ssl.conf <<EOF +# Mass dynamic virtual host configuration +# Generated by: httpd-ssl-conf $* +Include conf/vhost-ssl.conf + +# Declare wildcard SSL certificates used in this virtual host +SSLCACertificateFile "$root/cert/ca.crt" +SSLCertificateChainFile "$root/cert/ca.crt" +SSLCertificateFile "$root/cert/vhost.crt" +SSLCertificateKeyFile "$root/cert/vhost.key" + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-start b/sca-cpp/branches/lightweight-sca/modules/http/httpd-start new file mode 100755 index 0000000000..e38c2f9a94 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +apachectl=`cat $here/httpd-apachectl.prefix` +$apachectl -E $root/logs/error_log -k start -d $root -f $root/conf/httpd.conf diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop b/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop new file mode 100755 index 0000000000..9010e4dd17 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +apachectl=`cat $here/httpd-apachectl.prefix` +$apachectl -k graceful-stop -d $root -f $root/conf/httpd.conf 2>/dev/null diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-test b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test new file mode 100755 index 0000000000..ab6ab5ad41 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test @@ -0,0 +1,42 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +rm -rf tmp +./httpd-conf tmp localhost 8090 htdocs +./httpd-event-conf tmp +./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 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf new file mode 100755 index 0000000000..0028576364 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf @@ -0,0 +1,38 @@ +#!/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 Tunnel configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +# Generate required modules list +cat >>$root/conf/modules.conf <<EOF +# Generated by: httpd-tunnel-ssl-conf $* +LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel$libsuffix + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf new file mode 100755 index 0000000000..0a061dbffa --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf @@ -0,0 +1,45 @@ +#!/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. + +# Configure HTTPD to run with the worker MPM +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +modules_prefix=`cat $here/httpd-modules.prefix` + +mkdir -p $root +mkdir -p $root/conf +cat >$root/conf/mpm.conf <<EOF +# Generated by: httpd-worker-conf $* +# Use HTTPD worker MPM +LoadModule mpm_worker_module ${modules_prefix}/modules/mod_mpm_worker.so + +EOF + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + cat >>$root/conf/mpm.conf <<EOF +# Generated by: httpd-conf $* +# Set thread stack size +ThreadStackSize 2097152 + +EOF +fi + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp new file mode 100644 index 0000000000..6470d6c587 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp @@ -0,0 +1,753 @@ +/* + * 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. + */ + +extern "C" { +#include <apr_strings.h> +#include <apr_fnmatch.h> +#include <apr_lib.h> +#define APR_WANT_STRFUNC +#include <apr_want.h> +#include <apr_base64.h> + +#include <httpd.h> +// Hack to workaround compile error with CLang/LLVM +#undef strtoul +// Hack to workaround compile error with HTTPD 2.3.8 +#define new new_ +#include <http_config.h> +#undef new +#include <http_core.h> +#include <http_connection.h> +#include <http_request.h> +// Ignore conversion warnings in HTTPD 2.3.15 header +#ifdef WANT_MAINTAINER_WARNINGS +#ifndef IS_DARWIN +#pragma GCC diagnostic ignored "-Wconversion" +#endif +#endif +#include <http_protocol.h> +// Re-enable conversion warnings +#ifdef WANT_MAINTAINER_WARNINGS +#ifndef IS_DARWIN +#pragma GCC diagnostic warning "-Wconversion" +#endif +#endif +// Hack to workaround compile error with HTTPD 2.3.8 +#define aplog_module_index aplog_module_index = 0 +#include <http_log.h> +#undef aplog_module_index +#undef APLOG_MODULE_INDEX +#define APLOG_MODULE_INDEX (aplog_module_index ? *aplog_module_index : APLOG_NO_MODULE) +#include <http_main.h> +#include <util_script.h> +#include <util_md5.h> +#include <http_config.h> +#include <http_log.h> +#include <ap_mpm.h> +#include <mod_core.h> +#include <ap_provider.h> +#include <mod_auth.h> +#include <mod_session.h> +} + +#include "string.hpp" +#include "stream.hpp" +#include "sstream.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "http.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(p, 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); +} + +/** + * Returns a directory-scoped module configuration. + */ +template<typename C> void* makeDirConf(apr_pool_t *p, char* d) { + return new (gc_new<C>(p)) C(p, d); +} + +template<typename C> const C& dirConf(const request_rec* r, const module* mod) { + return *(C*)ap_get_module_config(r->per_dir_config, mod); +} + +template<typename C> C& dirConf(const void* c) { + return *(C*)c; +} + +/** + * Returns a request-scoped module configuration. + */ +template<typename C> C& makeRequestConf(const request_rec* r, const module* mod) { + C* c = new (gc_new<C>(r->pool)) C(r->pool, r); + ap_set_module_config(r->request_config, mod, c); + return *c; +} + +template<typename C> C& requestConf(const request_rec* r, const module* mod) { + C* c = (C*)ap_get_module_config(r->request_config, mod); + if (c == NULL) + return makeRequestConf<C>(r, mod); + return *c; +} + +/** + * Return the host name for a server. + */ +const string hostName(const server_rec* s, const string& def = "localhost") { + return s->server_hostname != NULL? s->server_hostname : def; +} + +/** + * Return the host name from an HTTP request. + */ +const string hostName(request_rec* r, const string& def = "localhost") { + const char* fh = apr_table_get(r->headers_in, "X-Forwarded-Server"); + if (fh != NULL) + return fh; + const char* h = ap_get_server_name(r); + return h != NULL? h : (r->server->server_hostname != NULL? r->server->server_hostname : def); +} + +/** + * Convert a host name to a realm. + */ +const string realm(const string& host) { + const string pre = substr(host, 0, 4); + return pre == "www." || pre == "ww1." || pre == "ww2." || pre == "ww3."? substr(host, 4) : host; +} + +/** + * Return the protocol scheme for a server. + */ +const string scheme(const server_rec* s, const string& def = "http") { + return s->server_scheme != NULL? s->server_scheme : def; +} + +/** + * Return the protocol scheme from an HTTP request. + */ +const string scheme(request_rec* r, const string& def = "http") { + const char* fs = apr_table_get(r->headers_in, "X-Forwarded-HTTPS"); + if (fs != NULL) + return !strcmp(fs, "on")? "https" : "http"; + return r->server->server_scheme != NULL? r->server->server_scheme : def; +} + +/** + * Return the port number for a server. + */ +const int port(const server_rec* s, const int def = 80) { + return s->port != 0? s->port : def; +} + +/** + * Return the port number from an HTTP request. + */ +const int port(request_rec* r, const int def = 80) { + const char* fp = apr_table_get(r->headers_in, "X-Forwarded-Port"); + if (fp != NULL) + return atoi(fp); + const int p = ap_get_server_port(r); + return p != 0? p : def; +} + +/** + * Return the name of a server. + */ +const string serverName(const server_rec* s, const string& def = "localhost") { + ostringstream n; + const string sc = scheme(s); + const string h = hostName(s, def); + const int p = port(s, sc == "https"? 443 : 80); + n << sc << "://" << h; + if (!((sc == "http" && p == 80) || (sc == "https" && p == 443))) + n << ":" << p; + n << (s->path != NULL? string(s->path, s->pathlen) : ""); + return str(n); +} + +/** + * Determine the name of a server from an HTTP request. + */ +const string serverName(request_rec* r, const string& def = "localhost") { + ostringstream n; + const string s = scheme(r); + const string h = hostName(r, def); + const int p = port(r, s == "https"? 443 : 80); + n << s << "://" << h; + if (!((s == "http" && p == 80) || (s == "https" && p == 443))) + n << ":" << p; + n << (r->server->path != NULL? string(r->server->path, r->server->pathlen) : ""); + return str(n); +} + +/** + * Return true if a request is targeting a virtual host. + */ +const bool isVhostRequest(const server_rec* s, const string& d, request_rec* r) { + const string rh = hostName(r); + return rh != hostName(s) && http::topDomain(rh) == d; +} + +/** + * Return the content type of a request. + */ +const string contentType(const request_rec* r) { + const char* ct = apr_table_get(r->headers_in, "Content-Type"); + if (ct == NULL) + return ""; + return ct; +} + +/** + * Return the cookie header of a request. + */ +const string cookie(const request_rec* r) { + const char* c = apr_table_get(r->headers_in, "Cookie"); + if (c == NULL) + return ""; + return c; +} + +/** + * 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)); +} + +/** + * Convert a URI to an absolute URL. + */ +const string url(const string& uri, request_rec* r) { + if (contains(uri, "://")) + return uri; + ostringstream n; + const string s = scheme(r); + const string h = hostName(r, "localhost"); + const int p = port(r, s == "https"? 443 : 80); + n << s << "://" << h; + if (!((s == "http" && p == 80) || (s == "https" && p == 443))) + n << ":" << p; + n << uri; + return str(n); +} + +/** + * Convert a URI and a path to an absolute URL. + */ +const string url(const string& uri, const list<value>& p, request_rec* r) { + return url(uri + path(p), r); +} + +/** + * Escape a URI. + */ +const char escape_c2x[] = "0123456789ABCDEF"; +const string escape(const string& uri) { + debug(uri, "httpd::escape::uri"); + char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(uri) + 3); + const unsigned char* s = (const unsigned char *)c_str(uri); + unsigned char* d = (unsigned char*)copy; + unsigned c; + while ((c = *s)) { + if (apr_isalnum(c) || c == '_') + *d++ = (unsigned char)c; + else if (c == ' ') + *d++ = '+'; + else { + *d++ = '%'; + *d++ = escape_c2x[c >> 4]; + *d++ = escape_c2x[c & 0xf]; + } + ++s; + } + *d = '\0'; + debug(copy, "httpd::escape::result"); + return copy; +} + +/** + * Unescape a URI. + */ +const string unescape(const string& uri) { + debug(uri, "httpd::unescape::uri"); + char* b = const_cast<char*>(c_str(string(c_str(uri)))); + ap_unescape_url(b); + debug(b, "httpd::unescape::result"); + return b; +} + +/** + * Unescape a list of key of value pairs representing query args. + */ +const list<value> unescapeArg(const list<value> a) { + return mklist<value>(car(a), unescape(cadr(a))); +} + +const list<list<value> > unescapeArgs(const list<list<value> > args) { + debug(args, "httpd::unescape::args"); + const list<list<value> > uargs = map<list<value>, list<value>>(unescapeArg, args); + debug(uargs, "httpd::unescape::result"); + return uargs; +} + +/** + * Returns a list of key value pairs from the args in a query string. + */ +const list<value> queryArg(const string& s) { + debug(s, "httpd::queryArg::string"); + const list<string> t = tokenize("=", s); + if (isNil(cdr(t))) + return mklist<value>(c_str(car(t)), ""); + return mklist<value>(c_str(car(t)), cadr(t)); +} + +const string fixupQueryArgs(const string& a) { + const list<string> t = tokenize("?", a); + if (isNil(t) || isNil(cdr(t))) + return a; + return join("&", t); +} + +const list<list<value> > queryArgs(const string& a) { + return map<string, list<value>>(queryArg, tokenize("&", fixupQueryArgs(a))); +} + +/** + * Returns a list of key value pairs from the args in an HTTP request. + */ +const list<list<value> > queryArgs(const request_rec* r) { + if (r->args == NULL) + return list<list<value> >(); + return queryArgs(r->args); +} + +/** + * 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 size_t n = ap_get_client_block(r, b, sizeof(b)); + if (n <= 0) + return list<string>(); + return cons(string(b, n), read(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>(ls); + ostringstream os; + write(content(ls), os); + const string ob(str(os)); + + // Make sure browsers come back and check for updated dynamic content + apr_table_set(r->headers_out, "Cache-Control", "must-revalidate, max-age=0"); + apr_table_set(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT"); + apr_table_set(r->subprocess_env, "private-cache", "1"); + + // Compute and return an Etag for the returned content + const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(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; + debug(r->status, "httpd::writeResult::status"); + return OK; + } + + debug(r->status, "httpd::writeResult::status"); + debug(ct, "httpd::writeResult::contentType"); + debug(ob, "httpd::writeResult::content"); + ap_set_content_type(r, apr_pstrdup(r->pool, c_str(ct))); + ap_rwrite(c_str(ob), (int)length(ob), r); + return OK; +} + +/** + * Report a request execution status. + */ +const int reportStatus(const failable<int>& rc) { + debug(rc, "httpd::reportStatus::rc"); + if (!hasContent(rc)) { + const int r = rcode(rc); + return r == -1 ? HTTP_INTERNAL_SERVER_ERROR : r; + } + return content(rc); +} + +/** + * 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 request 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 internal redirect request. + * Similar to httpd/modules/http/http_request.c::internal_internal_redirect. + */ +const failable<request_rec*> internalRedirectRequest(const string& nr_uri, request_rec* r) { + if (ap_is_recursion_limit_exceeded(r)) + return mkfailure<request_rec*>("Redirect recursion limit exceeded", 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->filename = apr_pstrdup(nr->pool, c_str(string("/redirected:") + 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; + nr->user = r->user; + + // 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*>("Error handling internal redirect", rrc); + + return nr; +} + +/** + * Process an HTTPD internal redirect request. + * Similar to httpd/modules/http/http_request.c::ap_internal_redirect. + */ +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 internal redirect request. + */ +const int internalRedirect(const string& uri, request_rec* r) { + debug(uri, "httpd::internalRedirect"); + const failable<request_rec*> nr = httpd::internalRedirectRequest(uri, r); + if (!hasContent(nr)) + return rcode(nr); + return httpd::internalRedirect(content(nr)); +} + +/** + * Create an HTTPD sub request. + * Similar to httpd/server/request.c::make_sub_request + */ +const failable<request_rec*> internalSubRequest(const string& nr_uri, request_rec* r) { + if (ap_is_recursion_limit_exceeded(r)) + return mkfailure<request_rec*>("Redirect recursion limit exceeded", HTTP_INTERNAL_SERVER_ERROR); + + // Create a new sub pool + apr_pool_t *nrp; + apr_pool_create(&nrp, r->pool); + apr_pool_tag(nrp, "subrequest"); + + // Create a new POST request + request_rec* nr = (request_rec*)apr_pcalloc(nrp, sizeof(request_rec)); + nr->connection = r->connection; + nr->server = r->server; + nr->pool = nrp; + nr->method = "POST"; + nr->method_number = M_POST; + nr->allowed_methods = ap_make_method_list(nr->pool, 2); + ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri))); + nr->filename = apr_pstrdup(nr->pool, c_str(string("/subreq:") + nr_uri)); + nr->request_config = ap_create_request_config(r->pool); + nr->per_dir_config = r->server->lookup_defaults; + + // Inherit some of the protocol info from the parent request + nr->the_request = r->the_request; + nr->hostname = r->hostname; + nr->request_time = r->request_time; + nr->allowed = r->allowed; + nr->status = HTTP_OK; + nr->assbackwards = r->assbackwards; + nr->header_only = r->header_only; + nr->protocol = const_cast<char*>("INCLUDED"); + nr->hostname = r->hostname; + nr->request_time = r->request_time; + nr->main = r; + nr->headers_in = apr_table_make(r->pool, 12); + nr->headers_out = apr_table_make(r->pool, 12); + nr->err_headers_out = apr_table_make(nr->pool, 5); + nr->subprocess_env = r->subprocess_env; + nr->subprocess_env = apr_table_copy(nr->pool, r->subprocess_env); + nr->notes = apr_table_make(r->pool, 5); + 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 = 0; + nr->vlist_validator = r->vlist_validator; + nr->user = r->user; + + // 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; + ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection); + + // Run create request hook + ap_run_create_request(nr); + nr->used_path_info = AP_REQ_DEFAULT_PATH_INFO; + + return nr; +} + +/** + * Return an HTTP external redirect request. + */ +const int externalRedirect(const string& uri, request_rec* r) { + debug(uri, "httpd::externalRedirect"); + r->status = HTTP_MOVED_TEMPORARILY; + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri))); + apr_table_setn(r->headers_out, "Cache-Control", "no-store"); + apr_table_addn(r->err_headers_out, "Cache-Control", "no-store"); + r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri)); + return HTTP_MOVED_TEMPORARILY; +} + +/** + * 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; +} + +#ifdef WANT_MAINTAINER_LOG + +/** + * Debug log. + */ + +/** + * Log an optional value. + */ +const char* debugOptional(const char* s) { + if (s == NULL) + return ""; + return s; +} + +/** + * Log a header + */ +int debugHeader(unused void* r, const char* key, const char* value) { + cdebug << " header key: " << key << ", value: " << value << endl; + return 1; +} + +/** + * Log an environment variable + */ +int debugEnv(unused void* r, const char* key, const char* value) { + cdebug << " var key: " << key << ", value: " << value << endl; + return 1; +} + +/** + * Log a note. + */ +int debugNote(unused void* r, const char* key, const char* value) { + cdebug << " note key: " << key << ", value: " << value << endl; + return 1; +} + +/** + * Log a request. + */ +const bool debugRequest(request_rec* r, const string& msg) { + gc_scoped_pool pool; + cdebug << msg << ":" << endl; + cdebug << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl; + cdebug << " uri: " << debugOptional(r->uri) << endl; + cdebug << " path info: " << debugOptional(r->path_info) << endl; + cdebug << " filename: " << debugOptional(r->filename) << endl; + cdebug << " uri tokens: " << pathTokens(r->uri) << endl; + cdebug << " args: " << debugOptional(r->args) << endl; + cdebug << " server: " << debugOptional(r->server->server_hostname) << endl; + cdebug << " protocol: " << debugOptional(r->protocol) << endl; + cdebug << " method: " << debugOptional(r->method) << endl; + cdebug << " method number: " << r->method_number << endl; + cdebug << " content type: " << contentType(r) << endl; + cdebug << " content encoding: " << debugOptional(r->content_encoding) << endl; + apr_table_do(debugHeader, r, r->headers_in, NULL); + cdebug << " user: " << debugOptional(r->user) << endl; + cdebug << " auth type: " << debugOptional(r->ap_auth_type) << endl; + apr_table_do(debugEnv, r, r->subprocess_env, NULL); + apr_table_do(debugNote, r, r->notes, NULL); + return true; +} + +#define debug_httpdRequest(r, msg) do { if (debug_islogging()) httpd::debugRequest(r, msg); } while(0) + +#else + +#define debug_httpdRequest(r, msg) + +#endif + +} +} + +#endif /* tuscany_httpd_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf new file mode 100755 index 0000000000..2da8f4f836 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf @@ -0,0 +1,62 @@ +#!/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 mass dynamic virtual hosting configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +vroot=`echo "import os; print os.path.realpath('$2')" | python` +vhtdocs=$3 + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` +addr=`echo $conf | awk '{ print $7 }'` +port=`$here/httpd-addr port $addr` +pport=`$here/httpd-addr pport $addr` +vhost=`$here/httpd-addr vhost $addr` + +htdocs=`echo $conf | awk '{ print $8 }'` +mkdir -p $htdocs +htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python` + +cat >>$root/conf/httpd.conf <<EOF +# Generated by: mass-host-conf $* +# Enable mass dynamic virtual hosting +NameVirtualHost $vhost + +<VirtualHost $vhost> +ServerName http://vhost.$host:$pport +ServerAlias *.$host + +# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path> +AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2 + +Include conf/dvhost.conf + +# Configure authentication +Include conf/noauth.conf +Include conf/locauth.conf +Include conf/pubauth.conf +Include conf/adminauth.conf + +</VirtualHost> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf new file mode 100755 index 0000000000..68bd5b4606 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf @@ -0,0 +1,68 @@ +#!/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 mass dynamic virtual hosting configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +ssladdr=`echo $sslconf | awk '{ print $6 }'` +sslport=`$here/httpd-addr port $ssladdr` +sslpport=`$here/httpd-addr pport $ssladdr` +sslvhost=`$here/httpd-addr vhost $ssladdr` + +vhostconf=`cat $root/conf/httpd.conf | grep "# Generated by: mass-host-conf"` +vroot=`echo $vhostconf | awk '{ print $6 }'`; vroot=`echo "import os; print os.path.realpath('$vroot')" | python` +vhtdocs=`echo $vhostconf | awk '{ print $7 }'` + +htdocs=`echo $conf | awk '{ print $8 }'` +mkdir -p $htdocs +htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python` + +cat >>$root/conf/httpd.conf <<EOF +# Generated by: mass-host-ssl-conf $* +# Enable mass dynamic virtual hosting over HTTPS +SSLStrictSNIVHostCheck Off + +# HTTPS dynamic virtual host +NameVirtualHost $sslvhost +<VirtualHost $sslvhost> +ServerName https://vhost.$host:$sslpport +ServerAlias *.$host + +# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path> +AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2 + +Include conf/dvhost-ssl.conf + +# Configure authentication +Include conf/noauth-ssl.conf +Include conf/locauth-ssl.conf +Include conf/pubauth-ssl.conf +Include conf/adminauth-ssl.conf + +# Configure tracking +Include conf/tracking-ssl.conf + +</VirtualHost> + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-css b/sca-cpp/branches/lightweight-sca/modules/http/minify-css new file mode 100755 index 0000000000..734d041aba --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-css @@ -0,0 +1,34 @@ +#!/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. + +# Minify a CSS file +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +pagespeed_prefix=`cat $here/pagespeed.prefix` + +if [ "${pagespeed_prefix}" = "" ]; then + minify_css="cat" +else + minify_css="${pagespeed_prefix}/jsmin_bin" +fi + +css="$1" +mincss="$2" + +${minify_css} <${css} >${mincss} + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-html b/sca-cpp/branches/lightweight-sca/modules/http/minify-html new file mode 100755 index 0000000000..31bb329d94 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-html @@ -0,0 +1,34 @@ +#!/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. + +# Minify an HTML file +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +pagespeed_prefix=`cat $here/pagespeed.prefix` + +if [ "${pagespeed_prefix}" = "" ]; then + minify_html="cp" +else + minify_html="${pagespeed_prefix}/minify_html_bin" +fi + +html="$1" +minhtml="$2" + +${minify_html} ${html} ${minhtml} + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-js b/sca-cpp/branches/lightweight-sca/modules/http/minify-js new file mode 100755 index 0000000000..5e7cbb1bc0 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-js @@ -0,0 +1,34 @@ +#!/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. + +# Minify a JS file +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +pagespeed_prefix=`cat $here/pagespeed.prefix` + +if [ "${pagespeed_prefix}" = "" ]; then + minify_js="cat" +else + minify_js="${pagespeed_prefix}/jsmin_bin" +fi + +js="$1" +minjs="$2" + +${minify_js} <${js} >${minjs} + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp new file mode 100644 index 0000000000..2e308ecedb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp @@ -0,0 +1,478 @@ +/* + * 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 for Tuscany Open authentication. + * + * This module allows multiple authentication mechanisms to co-exist in a + * single Web site: + * - OAuth1 using Tuscany's mod-tuscany-oauth1 + * - OAuth2 using Tuscany's mod-tuscany-oauth2 + * - OpenID using mod_auth_openid + * - Form-based using HTTPD's mod_auth_form + * - SSL certificate using SSLFakeBasicAuth and mod_auth_basic + */ + +#include <sys/stat.h> + +#define WANT_HTTPD_LOG 1 +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "httpd.hpp" +#include "http.hpp" +#include "openauth.hpp" + + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_openauth; +} + +namespace tuscany { +namespace openauth { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) { + } + + const gc_pool p; + server_rec* server; +}; + +/** + * Authentication provider configuration. + */ +class AuthnProviderConf { +public: + AuthnProviderConf() : name(), provider(NULL) { + } + AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) { + } + + string name; + const authn_provider* provider; +}; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf(apr_pool_t* p, char* d) : p(p), dir(d), enabled(false), login("") { + } + + const gc_pool p; + const char* dir; + bool enabled; + string login; + list<AuthnProviderConf> apcs; +}; + +#ifdef WANT_MAINTAINER_LOG + +/** + * Log session entries. + */ +int debugSessionEntry(unused void* r, const char* key, const char* value) { + cdebug << " session key: " << key << ", value: " << value << endl; + return 1; +} + +const bool debugSession(request_rec* r, session_rec* z) { + apr_table_do(debugSessionEntry, r, z->entries, NULL); + return true; +} + +#define debug_authSession(r, z) if (debug_islogging()) openauth::debugSession(r, z) + +#else + +#define debug_authSession(r, z) + +#endif + +/** + * Run the authnz hooks to authenticate a request. + */ +const failable<int> checkAuthnzProviders(const string& user, const string& pw, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (isNil(apcs)) + return mkfailure<int>("Authentication failure for: " + user); + const AuthnProviderConf apc = car<AuthnProviderConf>(apcs); + if (apc.provider == NULL || !apc.provider->check_password) + return checkAuthnzProviders(user, pw, r, cdr(apcs)); + + apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name)); + const authn_status auth_result = apc.provider->check_password(r, c_str(user), c_str(pw)); + apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); + if (auth_result != AUTH_GRANTED) + return checkAuthnzProviders(user, pw, r, cdr(apcs)); + return OK; +} + +const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* r, const DirConf& dc) { + if (substr(user, 0, 1) == "/" && pw == "password") + return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + + if (isNil(dc.apcs)) { + const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); + return checkAuthnzProviders(user, pw, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider))); + } + return checkAuthnzProviders(user, pw, r, dc.apcs); +} + +/** + * Return the user info from a form auth encrypted session cookie. + */ +static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL; +static int (*ap_session_get_fn) (request_rec * r, session_rec * z, const char *key, const char **value) = NULL; + +const failable<value> userInfoFromSession(const string& realm, request_rec* r) { + debug("modopenauth::userInfoFromSession"); + if (ap_session_load_fn == NULL) + ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load); + session_rec *z = NULL; + ap_session_load_fn(r, &z); + if (z == NULL) + return mkfailure<value>("Couldn't retrieve user session"); + debug_authSession(r, z); + + if (ap_session_get_fn == NULL) + ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get); + const char* user = NULL; + ap_session_get_fn(r, z, c_str(realm + "-user"), &user); + if (user == NULL) + return mkfailure<value>("Couldn't retrieve user id"); + const char* pw = NULL; + ap_session_get_fn(r, z, c_str(realm + "-pw"), &pw); + if (pw == NULL) + return mkfailure<value>("Couldn't retrieve password"); + return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", string(user)), mklist<value>("password", string(pw)))); +} + +/** + * Return the user info from a form auth session cookie. + */ +const failable<value> userInfoFromCookie(const value& sid, const string& realm, request_rec* r) { + const list<list<value>> info = httpd::queryArgs(sid); + debug(info, "modopenauth::userInfoFromCookie::info"); + const list<value> user = assoc<value>(realm + "-user", info); + if (isNil(user)) + return userInfoFromSession(realm, r); + const list<value> pw = assoc<value>(realm + "-pw", info); + if (isNil(pw)) + return mkfailure<value>("Couldn't retrieve password"); + return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", cadr(user)), mklist<value>("password", cadr(pw)))); +} + +/** + * Return the user info from a basic auth header. + */ +const failable<value> userInfoFromHeader(const char* header, const string& realm, request_rec* r) { + debug(header, "modopenauth::userInfoFromHeader::header"); + if (strcasecmp(ap_getword(r->pool, &header, ' '), "Basic")) + return mkfailure<value>("Wrong authentication scheme"); + + while (apr_isspace(*header)) + header++; + char *decoded_line = (char*)apr_palloc(r->pool, apr_base64_decode_len(header) + 1); + int length = apr_base64_decode(decoded_line, header); + decoded_line[length] = '\0'; + + const string user(ap_getword_nulls(r->pool, const_cast<const char**>(&decoded_line), ':')); + const string pw(decoded_line); + + return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", user), mklist<value>("password", pw))); +} + +/** + * Handle an authenticated request. + */ +const failable<int> authenticated(const list<list<value> >& info, request_rec* r) { + debug(info, "modopenauth::authenticated::info"); + + // Store user info in the request + const list<value> realm = assoc<value>("realm", info); + if (isNil(realm) || isNil(cdr(realm))) + return mkfailure<int>("Couldn't retrieve realm"); + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm)))); + + const list<value> id = assoc<value>("id", info); + if (isNil(id) || isNil(cdr(id))) + return mkfailure<int>("Couldn't retrieve user id"); + r->user = apr_pstrdup(r->pool, c_str(cadr(id))); + + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(id)))); + return OK; +} + +/** + * Check user authentication. + */ +static int checkAuthn(request_rec *r) { + gc_scoped_pool pool(r->pool); + + // Decline if we're not enabled or AuthType is not set to Open + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); + if (!dc.enabled) + return DECLINED; + const char* atype = ap_auth_type(r); + if (atype == NULL || strcasecmp(atype, "Open")) + return DECLINED; + debug_httpdRequest(r, "modopenauth::checkAuthn::input"); + debug(atype, "modopenauth::checkAuthn::auth_type"); + + // Get the request args + const list<list<value> > args = httpd::queryArgs(r); + + // Get session id from the request + const maybe<string> sid = sessionID(r, "TuscanyOpenAuth"); + if (hasContent(sid)) { + // Decline if the session id was not created by this module + const string stype = substr(content(sid), 0, 7); + if (stype == "OAuth2_" || stype == "OAuth1_" || stype == "OpenID_") + return DECLINED; + + // Retrieve the auth realm + const char* aname = ap_auth_name(r); + if (aname == NULL) + return httpd::reportStatus(mkfailure<int>("Missing AuthName")); + + // Extract user info from the session id + const failable<value> userinfo = userInfoFromCookie(content(sid), aname, r); + if (hasContent(userinfo)) { + + // Try to authenticate the request + const value uinfo = content(userinfo); + const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc); + if (!hasContent(authz)) { + + // Authentication failed, redirect to login page + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(login(dc.login, value(), 1, r)); + } + + // Successfully authenticated, store the user info in the request + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authenticated(uinfo, r)); + } + } + + // Get basic auth header from the request + const char* header = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authorization" : "Authorization"); + if (header != NULL) { + + // Retrieve the auth realm + const char* aname = ap_auth_name(r); + if (aname == NULL) + return httpd::reportStatus(mkfailure<int>("Missing AuthName")); + + // Extract user info from the session id + const failable<value> info = userInfoFromHeader(header, aname, r); + if (hasContent(info)) { + + // Try to authenticate the request + const value uinfo = content(info); + const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc); + if (!hasContent(authz)) { + + // Authentication failed, redirect to login page + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(login(dc.login, value(), 1, r)); + } + + // Successfully authenticated, store the user info in the request + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authenticated(uinfo, r)); + } + } + + // Decline if the request is for another authentication provider + if (!isNil(assoc<value>("openid_identifier", args))) + return DECLINED; + + // Redirect to the login page, unless we have a session id from another module + if (hasContent(sessionID(r, "TuscanyOpenIDAuth")) || + hasContent(sessionID(r, "TuscanyOAuth1")) || + hasContent(sessionID(r, "TuscanyOAuth2"))) + return DECLINED; + + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(login(dc.login, value(), value(), r)); +} + +/** + * Save the auth session cookie in the response. + */ +static int sessionCookieSave(request_rec* r, session_rec* z) { + gc_scoped_pool pool(r->pool); + + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); + if (!dc.enabled) + return DECLINED; + + debug(c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))), "modopenauth::setcookie"); + apr_table_set(r->err_headers_out, "Set-Cookie", c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r)))); + return OK; +} + +/** + * Load the auth session cookie from the request. Similar + */ +static int sessionCookieLoad(request_rec* r, session_rec** z) { + gc_scoped_pool pool(r->pool); + + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); + if (!dc.enabled) + return DECLINED; + + // First look in the notes + const char* note = apr_pstrcat(r->pool, "mod_openauth", "TuscanyOpenAuth", NULL); + session_rec* zz = (session_rec*)(void*)apr_table_get(r->notes, note); + if (zz != NULL) { + *z = zz; + return OK; + } + + // Parse the cookie + const maybe<string> sid = openauth::sessionID(r, "TuscanyOpenAuth"); + + // Create a new session + zz = (session_rec*)apr_pcalloc(r->pool, sizeof(session_rec)); + zz->pool = r->pool; + zz->entries = apr_table_make(r->pool, 10); + zz->encoded = hasContent(sid)? c_str(content(sid)) : NULL; + zz->uuid = (apr_uuid_t *) apr_pcalloc(r->pool, sizeof(apr_uuid_t)); + *z = zz; + + // Store it in the notes + apr_table_setn(r->notes, note, (char*)zz); + + return OK; +} + +/** + * Process the module configuration. + */ +int postConfigMerge(ServerConf& mainsc, server_rec* s) { + if (s == NULL) + return OK; + debug(httpd::serverName(s), "modopenauth::postConfigMerge::serverName"); + + return postConfigMerge(mainsc, s->next); +} + +int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { + gc_scoped_pool pool(p); + + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_openauth); + debug(httpd::serverName(s), "modopenauth::postConfig::serverName"); + + // Merge server configurations + return postConfigMerge(sc, s); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* s) { + gc_scoped_pool pool(p); + + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_openauth); + if(psc == NULL) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_openauth loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + ServerConf& sc = *psc; + + // Merge the updated configuration into the virtual hosts + postConfigMerge(sc, s->next); +} + +/** + * Configuration commands. + */ +const char* confEnabled(cmd_parms *cmd, void *c, const int arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + dc.enabled = (bool)arg; + return NULL; +} +const char* confLogin(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + dc.login = arg; + return NULL; +} +const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + + // Lookup and cache the Authn provider + const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION); + if (provider == NULL) + return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg); + if (!provider->check_password) + return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg); + dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider))); + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_ITERATE("AuthOpenAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"), + AP_INIT_FLAG("AuthOpenAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "Tuscany Open Auth authentication On | Off"), + AP_INIT_TAKE1("AuthOpenAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "Tuscany Open Auth login page"), + {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_check_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); + ap_hook_session_load(sessionCookieLoad, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_session_save(sessionCookieSave, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_openauth = { + STANDARD20_MODULE_STUFF, + // dir config and merger + tuscany::httpd::makeDirConf<tuscany::openauth::DirConf>, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::openauth::ServerConf>, NULL, + // commands and hooks + tuscany::openauth::commands, tuscany::openauth::registerHooks +}; + +} diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf new file mode 100755 index 0000000000..5914bd1df4 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf @@ -0,0 +1,44 @@ +#!/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 mod-security audit configuration. +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +mkdir -p $root/tmp + +cat >>$root/conf/mod-security.conf <<EOF +# Generated by: mod-security-audit-conf $* +# Enable mod-security audit log +SecAuditEngine RelevantOnly +SecAuditLogRelevantStatus "^(?:5|4(?!04))" +SecAuditLogParts ABIJDEFHKZ +SecAuditLogType Serial +Include conf/mod-security-audit-log.conf + +EOF + +# Configure audit logging +cat >$root/conf/mod-security-audit-log.conf <<EOF +# Generated by: mod-security-audit-conf $* +SecAuditLog $root/logs/secaudit_log + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf new file mode 100755 index 0000000000..5d03fc5cfb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf @@ -0,0 +1,184 @@ +#!/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 mod-security configuration. +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +modules_prefix=`cat $here/httpd-modules.prefix` +modsecurity_prefix=`cat $here/modsecurity.prefix` + +mkdir -p $root/tmp + +cat >>$root/conf/modules.conf <<EOF +# Generated by: mod-security-conf $* +# Load support for mod-security +LoadModule unique_id_module ${modules_prefix}/modules/mod_unique_id.so +LoadModule security2_module $modsecurity_prefix/lib/mod_security2.so + +EOF + +cat >>$root/conf/httpd.conf <<EOF +# Generated by: mod-security-conf $* +# Enable mod-security +Include conf/mod-security.conf + +EOF + +cat >$root/conf/mod-security.conf <<EOF +# Generated by: mod-security-conf $* +# Enable mod-security rules +SecRuleEngine On +SecDefaultAction "phase:2,pass,nolog,auditlog" + +#SecDebugLog $root/logs//modsec_debug_log +#SecDebugLogLevel 3 + +# Process request bodies +SecRequestBodyAccess Off +SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" +SecRule REQUEST_HEADERS:Content-Type "application/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" +SecRequestBodyLimit 13107200 +SecRequestBodyNoFilesLimit 131072 +SecRequestBodyInMemoryLimit 131072 +SecRequestBodyLimitAction Reject + +# Verify that we've correctly processed the request body +SecRule REQBODY_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" + +# By default be strict with what we accept in multipart/form-data request body +SecRule MULTIPART_STRICT_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart request body failed strict validation: \ +PE %{REQBODY_PROCESSOR_ERROR}, \ +BQ %{MULTIPART_BOUNDARY_QUOTED}, \ +BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ +DB %{MULTIPART_DATA_BEFORE}, \ +DA %{MULTIPART_DATA_AFTER}, \ +HF %{MULTIPART_HEADER_FOLDING}, \ +LF %{MULTIPART_LF_LINE}, \ +SM %{MULTIPART_SEMICOLON_MISSING}, \ +IQ %{MULTIPART_INVALID_QUOTING}, \ +IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ +IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + +# Did we see anything that might be a boundary? +SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'" + +# Avoid a potential RegEx DoS condition +SecPcreMatchLimit 50000 +SecPcreMatchLimitRecursion 50000 +SecRule TX:/^MSC_/ "!@streq 0" "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" + +# Detect slow DoS attacks +SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass, setvar:ip.slow_dos_counter=+1,expirevar:ip.slow_dos_counter=60" +SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client Connection Dropped due to high # of slow DoS alerts'" +SecWriteStateLimit 50 + +# Process response bodies +SecResponseBodyAccess Off +SecResponseBodyMimeType text/plain text/html text/xml application/xml +SecResponseBodyLimit 524288 +SecResponseBodyLimitAction ProcessPartial + +# The location where mod-security stores temporary files +SecTmpDir $root/tmp/ +SecDataDir $root/tmp/ + +# Use & as application/x-www-form-urlencoded parameter separator +SecArgumentSeparator & + +# Settle on version 0 (zero) cookies. +SecCookieFormat 0 + +# Enable anomaly scoring +SecAction "phase:1,id:'981206',t:none,nolog,pass,setvar:tx.anomaly_score_blocking=on" +SecAction "phase:1,id:'981207',t:none,nolog,pass, \ +setvar:tx.critical_anomaly_score=5, \ +setvar:tx.error_anomaly_score=4, \ +setvar:tx.warning_anomaly_score=3, \ +setvar:tx.notice_anomaly_score=2" +SecAction "phase:1,id:'981208',t:none,nolog,pass,setvar:tx.inbound_anomaly_score_level=10" +SecAction "phase:1,id:'981209',t:none,nolog,pass,setvar:tx.outbound_anomaly_score_level=8" + +# Paranoid mode +SecAction "phase:1,id:'981210',t:none,nolog,pass,setvar:tx.paranoid_mode=0" + +# HTTP policy settings +SecAction "phase:1,id:'981211',t:none,nolog,pass,setvar:tx.max_num_args=255" +SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_name_length=100" +SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_length=400" +SecAction "phase:1,t:none,nolog,pass,setvar:tx.total_arg_length=64000" +SecAction "phase:1,t:none,nolog,pass,setvar:tx.max_file_size=1048576" +SecAction "phase:1,t:none,nolog,pass,setvar:tx.combined_file_sizes=1048576" +SecAction "phase:1,id:'981212',t:none,nolog,pass, \ +setvar:'tx.allowed_methods=GET HEAD POST PUT OPTIONS DELETE CONNECT', \ +setvar:'tx.allowed_request_content_type=application/x-www-form-urlencoded multipart/form-data text/xml application/xml application/json application/json-rpc application/atom+xml', \ +setvar:'tx.allowed_http_versions=HTTP/0.9 HTTP/1.0 HTTP/1.1', \ +setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/', \ +setvar:'tx.restricted_headers=/Proxy-Connection/ /Lock-Token/ /Content-Range/ /Translate/ /via/ /if/'" + +# Brute force protection +SecAction "phase:1,id:'981214',t:none,nolog,pass, \ +setvar:'tx.brute_force_protected_urls=/login', \ +setvar:'tx.brute_force_burst_time_slice=60', \ +setvar:'tx.brute_force_counter_threshold=10', \ +setvar:'tx.brute_force_block_timeout=300'" + +# DoS protection +SecAction "phase:1,id:'981215',t:none,nolog,pass, \ +setvar:'tx.dos_burst_time_slice=60', \ +setvar:'tx.dos_counter_threshold=100', \ +setvar:'tx.dos_block_timeout=600'" + +# Check UTF-8 encoding +SecAction "phase:1,id:'981216',t:none,nolog,pass,setvar:tx.crs_validate_utf8_encoding=1" + +# Global and IP collections +SecRule REQUEST_HEADERS:User-Agent "^(.*)$" "phase:1,id:'981217',t:none,pass,nolog,t:sha1,t:hexEncode,setvar:tx.ua_hash=%{matched_var}" +SecRule REQUEST_HEADERS:x-forwarded-for "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" "phase:1,id:'981225',t:none,pass,nolog,capture,setvar:tx.real_ip=%{tx.1}" +SecRule &TX:REAL_IP "!@eq 0" "phase:1,id:'981226',t:none,pass,nolog,initcol:global=global,initcol:ip=%{tx.real_ip}_%{tx.ua_hash}" +SecRule &TX:REAL_IP "@eq 0" "phase:1,id:'981218',t:none,pass,nolog,initcol:global=global,initcol:ip=%{remote_addr}_%{tx.ua_hash}" + +# Include all base mod-security CRS rules +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_20_protocol_violations.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_xss_attacks.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_50_outbound.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_35_bad_robots.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_47_common_exceptions.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_60_correlation.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_40_generic_attacks.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_21_protocol_anomalies.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_30_http_policy.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_49_inbound_blocking.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_sql_injection_attacks.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_45_trojans.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_59_outbound_blocking.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_23_request_limits.conf +Include ${modsecurity_prefix}/base_rules/modsecurity_crs_42_tight_security.conf + +# Include some optional mod-security CRS rules +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_10_ignore_static.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_13_xml_enabler.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_25_cc_known.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_42_comment_spam.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_47_skip_outbound_checks.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_55_application_defects.conf + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp new file mode 100644 index 0000000000..b66cd29959 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.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 tunnel traffic over an HTTPS connection. + */ + +#include <sys/stat.h> + +#define WANT_HTTPD_LOG 1 +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "httpd.hpp" +#include "http.hpp" + +// Ignore cast align warnings in APR macros +#ifdef WANT_MAINTAINER_WARNINGS +#pragma GCC diagnostic ignored "-Wcast-align" +#endif + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel; +} + +namespace tuscany { +namespace httpd { +namespace modssltunnel { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) { + } + + const gc_pool p; + server_rec* server; + string pass; + string host; + string path; + string ca; + string cert; + string key; +}; + +extern "C" { +extern module AP_DECLARE_DATA core_module; +} + +/** + * Process the module configuration. + */ +int M_SSLTUNNEL; +int postConfigMerge(ServerConf& mainsc, apr_pool_t* p, server_rec* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel); + debug(httpd::serverName(s), "modssltunnel::postConfigMerge::serverName"); + + // Merge configuration from main server + if (length(sc.ca) == 0 && length(mainsc.ca) !=0) + sc.ca = mainsc.ca; + if (length(sc.cert) == 0 && length(mainsc.cert) !=0) + sc.cert = mainsc.cert; + if (length(sc.key) == 0 && length(mainsc.key) !=0) + sc.key = mainsc.key; + + // Parse the configured TunnelPass URI + if (length(sc.pass) != 0) { + apr_uri_t uri; + apr_status_t prc = apr_uri_parse(p, c_str(sc.pass), &uri); + if (prc != APR_SUCCESS) { + mkfailure<int>("Couldn't parse TunnelPass: " + sc.pass + ", " + http::apreason(prc)); + return prc; + } + sc.host = uri.hostname; + sc.path = uri.path; + } + return postConfigMerge(mainsc, p, s->next); +} + +int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { + gc_scoped_pool pool(p); + + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel); + debug(httpd::serverName(s), "modssltunnel::postConfig::serverName"); + + // Register the SSLTUNNEL method + M_SSLTUNNEL = ap_method_register(p, "SSLTUNNEL"); + + // Merge and process server configurations + return postConfigMerge(sc, p, s); +} + +/** + * Close a connection. + */ +const int close(conn_rec* conn, apr_socket_t* csock) { + debug("modssltunnel::close"); + apr_socket_close(csock); + conn->aborted = 1; + return OK; +} + +/** + * Abort a connection. + */ +const int abort(conn_rec* conn, apr_socket_t* csock, const string& reason) { + debug("modssltunnel::abort"); + apr_socket_close(csock); + conn->aborted = 1; + return httpd::reportStatus(mkfailure<int>(reason)); +} + +/** + * Tunnel traffic from a client connection to a target URL. + */ +int tunnel(conn_rec* conn, const string& ca, const string& cert, const string& key, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* ifilter, ap_filter_t* ofilter) { + + // Create input/output bucket brigades + apr_bucket_brigade* ib = apr_brigade_create(pool(p), conn->bucket_alloc); + apr_bucket_brigade* ob = apr_brigade_create(pool(p), conn->bucket_alloc); + + // Get client connection socket + apr_socket_t* csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module); + + // Open connection to target + http::CURLSession cs(ca, cert, key, "", 0); + const failable<bool> crc = http::connect(url, cs); + if (!hasContent(crc)) + return abort(conn, csock, reason(crc)); + apr_socket_t* tsock = http::sock(cs); + + // Send preamble + if (length(preamble) != 0) { + debug(preamble, "modssltunnel::tunnel::sendPreambleToTarget"); + const failable<bool> src = http::send(c_str(preamble), length(preamble), cs); + if (!hasContent(src)) + return abort(conn, csock, string("Couldn't send to target: ") + reason(src)); + } + + // Create a pollset for the client and target sockets + apr_pollset_t* pollset; + apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0); + if (cprc != APR_SUCCESS) + return abort(conn, csock, http::apreason(cprc)); + const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p); + apr_pollset_add(pollset, cpollfd); + const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p); + apr_pollset_add(pollset, tpollfd); + + // Relay traffic in both directions until end of stream + const apr_pollfd_t* pollfds = cpollfd; + apr_int32_t pollcount = 1; + for(;;) { + for (; pollcount > 0; pollcount--, pollfds++) { + if (pollfds->rtnevents & APR_POLLIN) { + if (pollfds->desc.s == csock) { + + // Receive buckets from client + const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); + if (getrc != APR_SUCCESS) + return abort(conn, csock, string("Couldn't receive from client")); + + for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) { + if (APR_BUCKET_IS_FLUSH(bucket)) + continue; + + // Client connection closed + if (APR_BUCKET_IS_EOS(bucket)) + return close(conn, csock); + + const char *data; + apr_size_t rl; + apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ); + if (rl > 0) { + debug(string(data, rl), "modssltunnel::tunnel::sendToTarget"); + + // Send to target + const failable<bool> src = http::send(data, rl, cs); + if (!hasContent(src)) + return abort(conn, csock, string("Couldn't send to target: ") + reason(src)); + } + } + apr_brigade_cleanup(ib); + } else { + + // Receive from target + char data[8192]; + const failable<size_t> frl = http::recv(data, sizeof(data), cs); + if (!hasContent(frl)) + return abort(conn, csock, string("Couldn't receive from target") + reason(frl)); + const size_t rl = content(frl); + + // Target connection closed + if (rl == 0) + return close(conn, csock); + + + // Send bucket to client + debug(string(data, rl), "modssltunnel::tunnel::sendToClient"); + APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc)); + if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS) + return abort(conn, csock, "Couldn't send data bucket to client"); + apr_brigade_cleanup(ob); + } + } + + // Error + if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) { + if (pollfds->desc.s == csock) + return abort(conn, csock, "Couldn't receive from client"); + else + return abort(conn, csock, "Couldn't receive from target"); + } + } + + // Poll the client and target sockets + debug("modssltunnel::tunnel::poll"); + apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds); + if (pollrc != APR_SUCCESS) + return abort(conn, csock, "Couldn't poll sockets"); + debug(pollcount, "modssltunnel::tunnel::pollfds"); + } + + // Close client connection + return close(conn, csock); +} + +/** + * Return the first connection filter in a list of filters. + */ +ap_filter_t* connectionFilter(ap_filter_t* f) { + if (f == NULL) + return f; + if (f->frec->ftype < AP_FTYPE_CONNECTION) + return connectionFilter(f->next); + return f; +} + +/** + * Process a client connection and relay it to a tunnel. + */ +int processConnection(conn_rec *conn) { + // Only allow configured virtual hosts + if (!conn->base_server->is_virtual) + return DECLINED; + if (ap_get_module_config(conn->base_server->module_config, &mod_tuscany_ssltunnel) == NULL) + return DECLINED; + + gc_scoped_pool pool(conn->pool); + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(conn->base_server, &mod_tuscany_ssltunnel); + if (length(sc.pass) == 0) + return DECLINED; + debug(sc.pass, "modssltunnel::processConnection::pass"); + + // Run the tunnel + const string preamble = string("SSLTUNNEL ") + sc.path + string(" HTTP/1.1\r\nHost: ") + sc.host + string("\r\n\r\n"); + debug(preamble, "modssltunnel::processConnection::preamble"); + return tunnel(conn, sc.ca, sc.cert, sc.key, sc.pass, preamble, gc_pool(conn->pool), connectionFilter(conn->input_filters), connectionFilter(conn->output_filters)); +} + +/** + * Tunnel a SSLTUNNEL request to a target host/port. + */ +int handler(request_rec* r) { + if (r->method_number != M_SSLTUNNEL) + return DECLINED; + + // Only allow HTTPS + if (strcmp(r->server->server_scheme, "https")) + return DECLINED; + + gc_scoped_pool pool(r->pool); + + // Build the target URL + debug(r->uri, "modssltunnel::handler::uri"); + const list<value> path(pathValues(r->uri)); + const string url = string(cadr(path)) + ":" + caddr(path); + debug(url, "modssltunnel::handler::target"); + + // Run the tunnel + return tunnel(r->connection, "", "", "", url, "", gc_pool(r->pool), connectionFilter(r->proto_input_filters), connectionFilter(r->proto_output_filters)); +} + +/** + * Configuration commands. + */ +const char* confTunnelPass(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel); + sc.pass = 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_ssltunnel); + 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_ssltunnel); + 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_ssltunnel); + sc.key = arg; + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TunnelPass", (const char*(*)())confTunnelPass, NULL, RSRC_CONF, "Tunnel server name"), + AP_INIT_TAKE1("TunnelSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "Tunnel SSL CA certificate file"), + AP_INIT_TAKE1("TunnelSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "Tunnel SSL certificate file"), + AP_INIT_TAKE1("TunnelSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "Tunnel 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_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_process_connection(processConnection, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel = { + STANDARD20_MODULE_STUFF, + // dir config and merger + NULL, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::httpd::modssltunnel::ServerConf>, NULL, + // commands and hooks + tuscany::httpd::modssltunnel::commands, tuscany::httpd::modssltunnel::registerHooks +}; + +} + +// Reenable cast align warnings +#ifdef WANT_MAINTAINER_WARNINGS +#pragma GCC diagnostic warning "-Wcast-align" +#endif + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf new file mode 100755 index 0000000000..f4715b3a1c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf @@ -0,0 +1,100 @@ +#!/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 form authentication configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then + pw=`cat $root/cert/ca.key | head -2 | tail -1` +else + pw="$3" +fi + +# Configure HTTPD mod_tuscany_openauth module +cat >>$root/conf/modules.conf <<EOF +# Generated by: open-auth-conf $* +# Load support for Open authentication +LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix + +EOF + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: open-auth-conf $* +# Disallow public access to server resources + +EOF + +# Generate form authentication configuration +cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: open-auth-conf $* +# Enable Tuscany open authentication +<Location /> +AuthType Open +AuthName "$host" +AuthOpenAuthProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +Session On +SessionCryptoPassphrase $pw +AuthOpenAuth On +AuthOpenAuthLoginPage /login/ +Require valid-user +</Location> + +# Use HTTPD form-based authentication +<Location /login/dologin> +AuthType Form +AuthName "$host" +AuthFormProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +AuthFormLoginRequiredLocation /login/?openauth_attempt=1 +AuthFormLogoutLocation / +Require valid-user +SetHandler form-login-handler +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp new file mode 100644 index 0000000000..3ffa88d362 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp @@ -0,0 +1,100 @@ +/* + * 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_openauth_hpp +#define tuscany_openauth_hpp + +/** + * Tuscany Open auth support utility functions. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../json/json.hpp" +#include "../http/httpd.hpp" +#include "../http/http.hpp" + +namespace tuscany { +namespace openauth { + +/** + * Return the session id from a request. + */ +const char* cookieName(const char* cs) { + if (*cs != ' ') + return cs; + return cookieName(cs + 1); +} +const maybe<string> sessionID(const list<string>& c, const string& key) { + if (isNil(c)) + return maybe<string>(); + const string cn = cookieName(c_str(car(c))); + const size_t i = find(cn, "="); + if (i < length(cn)) { + const list<string> kv = mklist<string>(substr(cn, 0, i), substr(cn, i+1)); + if (!isNil(kv) && !isNil(cdr(kv))) { + if (car(kv) == key) + return cadr(kv); + } + } + return sessionID(cdr(c), key); +} + +const maybe<string> sessionID(const request_rec* r, const string& key) { + const string c = httpd::cookie(r); + debug(c, "openauth::sessionid::cookies"); + if (length(c) == 0) + return maybe<string>(); + return sessionID(tokenize(";", c), key); +} + +/** + * Convert a session id to a cookie string. + */ +const string cookie(const string& key, const string& sid, const string& domain) { + const time_t t = time(NULL) + 86400; + char exp[32]; + strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t)); + const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + httpd::realm(domain) + "; path=/"; + debug(c, "openauth::cookie"); + return c; +} + +/** + * Redirect to the configured login page. + */ +const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* r) { + const list<list<value> > rarg = ref == string("/")? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNil(ref)? r->uri : ref, r)))); + const list<list<value> > aarg = isNil(attempt)? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_attempt", attempt)); + const list<list<value> > largs = append<list<value> >(rarg, aarg); + const string loc = isNil(largs)? httpd::url(page, r) : httpd::url(page, r) + string("?") + http::queryString(largs); + debug(loc, "openauth::login::uri"); + return httpd::externalRedirect(loc, r); +} + +} +} + +#endif /* tuscany_openauth_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf new file mode 100755 index 0000000000..718b96de0a --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf @@ -0,0 +1,31 @@ +#!/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. + +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` +user=$2 +pass=$3 + +httpd_prefix=`cat $here/httpd.prefix` + +# Create password file +touch $root/conf/httpd.passwd +$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd "$user" "$pass" 2>/dev/null + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf new file mode 100755 index 0000000000..0b63c9e481 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf @@ -0,0 +1,50 @@ +#!/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 proxy balancer configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +bal=$2 +if [ "$bal" = "" ]; then + bal="cluster" +fi +loc=$3 +if [ "$loc" = "" ]; then + loc="/" +fi + +cat >>$root/conf/vhost.conf <<EOF +# Generated by: proxy-pass-conf $* +# Enable load balancing +ProxyPass $loc balancer://$bal/ + +<Proxy balancer://$bal> +Require all granted +ProxySet lbmethod=byrequests +</Proxy> + +<Location $loc> +RequestHeader set X-Forwarded-HTTPS %{HTTPS}s +RequestHeader set X-Forwarded-Port %{SERVER_PORT}s +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf new file mode 100755 index 0000000000..377175328d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf @@ -0,0 +1,48 @@ +#!/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 proxy balancer configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +cat >>$root/conf/vhost.conf <<EOF +# Generated by: proxy-base-conf $* +# Do not proxy admin pages +ProxyPass /balancer-manager ! +ProxyPass /server-status ! +ProxyPass /proxy ! + +# Enable balancer manager +<Location /balancer-manager> +SetHandler balancer-manager +HostnameLookups on +</Location> + +EOF + +cat >>$root/conf/adminauth.conf <<EOF +# Generated by: proxy-base-conf $* +# Allow the server admin to manage the load balancer +<Location /balancer-manager> +Require user admin +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf new file mode 100755 index 0000000000..ce990bbfd3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf @@ -0,0 +1,60 @@ +#!/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 proxy balancer configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +cat >>$root/conf/vhost.conf <<EOF +# Generated by: proxy-conf $* +# Do not proxy admin pages +ProxyPass /balancer-manager ! +ProxyPass /server-status ! +ProxyPass /proxy ! + +# Enable load balancing +ProxyPass / balancer://cluster/ +<Proxy balancer://cluster> +Require all granted +ProxySet lbmethod=byrequests +</Proxy> + +<Location /> +RequestHeader set X-Forwarded-HTTPS %{HTTPS}s +RequestHeader set X-Forwarded-Port %{SERVER_PORT}s +</Location> + +# Enable balancer manager +<Location /balancer-manager> +SetHandler balancer-manager +HostnameLookups on +</Location> + +EOF + +cat >>$root/conf/adminauth.conf <<EOF +# Generated by: proxy-conf $* +# Allow the server admin to manage the load balancer +<Location /balancer-manager> +Require user admin +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf new file mode 100755 index 0000000000..a046a4fd08 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf @@ -0,0 +1,45 @@ +#!/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. + +# Add a proxy balancer member +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 +port=`$here/httpd-addr port $3` +if [ "$port" = "80" ]; then + portsuffix="" +else + portsuffix=":$port" +fi + +bal=$4 +if [ "$bal" = "" ]; then + bal="cluster" +fi + +cat >>$root/conf/vhost.conf <<EOF +# Generated by: proxy-member-conf $* +# Add proxy balancer member +BalancerMember balancer://$bal http://$host$portsuffix +ProxyPassReverse / http://$host$portsuffix/ + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf new file mode 100755 index 0000000000..ad7f26d83a --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf @@ -0,0 +1,67 @@ +#!/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 proxy balancer configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +cat >>$root/conf/vhost-ssl.conf <<EOF +# Generated by: proxy-ssl-conf $* +# Do not proxy admin pages +ProxyPass /balancer-manager ! +ProxyPass /server-status ! +ProxyPass /proxy ! + +# Enable load balancing +ProxyPass / balancer://sslcluster/ +<Proxy balancer://sslcluster> +Require all granted +ProxySet lbmethod=byrequests +</Proxy> + +<Location /> +RequestHeader set X-Forwarded-HTTPS %{HTTPS}s +RequestHeader set X-Forwarded-Port %{SERVER_PORT}s +</Location> + +# Enable balancer manager +<Location /balancer-manager> +SetHandler balancer-manager +HostnameLookups on +</Location> + +EOF + +cat >>$root/conf/svhost-ssl.conf <<EOF +# Generated by: proxy-ssl-conf $* +# Declare proxy SSL client certificates +#SSLProxyCACertificateFile "$root/cert/ca.crt" +#SSLProxyMachineCertificateFile "$root/cert/proxy.pem" + +EOF + +cat >>$root/conf/dvhost-ssl.conf <<EOF +# Generated by: proxy-ssl-conf $* +# Declare proxy SSL client certificates +#SSLProxyCACertificateFile "$root/cert/ca.crt" +#SSLProxyMachineCertificateFile "$root/cert/proxy.pem" + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf new file mode 100755 index 0000000000..cb42a1e9db --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf @@ -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. + +# Add a proxy balancer member +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 +sslport=`$here/httpd-addr port $3` +if [ "$sslport" = "443" ]; then + sslportsuffix="" +else + sslportsuffix=":$sslport" +fi + +cat >>$root/conf/vhost-ssl.conf <<EOF +# Generated by: proxy-ssl-member-conf $* +# Add proxy balancer member +BalancerMember balancer://sslcluster https://$host$sslportsuffix +ProxyPassReverse / https://$host$sslportsuffix/ + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf new file mode 100755 index 0000000000..17b766d986 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf @@ -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. + +# Add a proxy balancer member +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 +port=`$here/httpd-addr port $3` +if [ "$port" = "80" ]; then + portsuffix="" +else + portsuffix=":$port" +fi + +cat >>$root/conf/vhost-ssl.conf <<EOF +# Generated by: proxy-ssl-nossl-member-conf $* +# Add proxy balancer member +BalancerMember balancer://sslcluster http://$host$portsuffix +ProxyPassReverse / http://$host$portsuffix/ + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-test b/sca-cpp/branches/lightweight-sca/modules/http/proxy-test new file mode 100755 index 0000000000..0333dd280b --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-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. + +# Setup +rm -rf tmp +./httpd-conf tmp localhost 8091/8090 htdocs +./httpd-event-conf tmp +./httpd-start tmp +./httpd-conf tmp/proxy localhost 8090 tmp/proxy/htdocs +./httpd-event-conf tmp/proxy +./proxy-conf tmp/proxy +./proxy-member-conf tmp/proxy localhost 8091 +./httpd-start tmp/proxy +sleep 2 + +# Test +./curl-test 2>/dev/null +rc=$? + +# Cleanup +./httpd-stop tmp/proxy +./httpd-stop tmp +sleep 2 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf new file mode 100755 index 0000000000..bceca8f300 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf @@ -0,0 +1,96 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 + +# Don't override existing certificate +if [ -f $root/cert/ca.crt ]; then + exit 0 +fi + +# Generate openssl configuration +mkdir -p $root/cert +umask 0007 +cat >$root/cert/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 = $host +OU = authority +CN = $host +emailAddress = admin@$host + +[ v3_ca ] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +basicConstraints = CA:true + +[ca] +default_ca = ca_default + +[ca_default] +certificate = $root/cert/ca.crt +private_key = $root/cert/ca.key +serial = $root/cert/ca-serial +database = $root/cert/ca-database +new_certs_dir = $root/cert +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/cert/*.crt $root/cert/*.pem $root/cert/hash +rm -f $root/cert/ca-database +echo 1000 > $root/cert/ca-serial +touch $root/cert/ca-database + +# Generate the certification authority certificate +openssl req -new -x509 -config $root/cert/openssl-ca.conf -out $root/cert/ca.crt -keyout $root/cert/ca.key + +# Add to the hash directory and rehash +mkdir -p $root/cert/hash +cp $root/cert/ca.crt $root/cert/hash +perl /usr/bin/c_rehash $root/cert/hash + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf new file mode 100755 index 0000000000..9e785ec86e --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf @@ -0,0 +1,76 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +host=$2 +if [ "$3" != "" ]; then + certname=$3 +else + certname="server" +fi + +# Don't regenerate the certificate if it already exists +if [ -f $root/cert/$certname.crt ]; then + exit 0 +fi + +# Generate openssl configuration +mkdir -p $root/cert +umask 0007 +cat >$root/cert/openssl-cert-$certname.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 = $host +OU = $certname +CN = $host +emailAddress = admin@$host +EOF + +# Generate a certificate request +openssl req -new -config $root/cert/openssl-cert-$certname.conf -out $root/cert/$certname-req.crt -keyout $root/cert/$certname.key + +# Generate a certificate, signed with our test certification authority certificate +openssl ca -batch -config $root/cert/openssl-ca.conf -out $root/cert/$certname.crt -infiles $root/cert/$certname-req.crt + +# Export it to PKCS12 format, that's the format Web browsers want to import +openssl pkcs12 -export -passout pass: -out $root/cert/$certname.p12 -inkey $root/cert/$certname.key -in $root/cert/$certname.crt -certfile $root/cert/ca.crt + +# Convert the certificate to PEM format and concatenate the key to it, for use +# by mod_proxy +openssl x509 -in $root/cert/$certname.crt -out $root/cert/$certname.pem +cat $root/cert/$certname.key >> $root/cert/$certname.pem + +# Add to the hash directory and rehash +mkdir -p $root/cert/hash +cp $root/cert/$certname.crt $root/cert/hash +cp $root/cert/$certname.pem $root/cert/hash +perl /usr/bin/c_rehash $root/cert/hash + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find new file mode 100755 index 0000000000..7a4b4f0220 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find @@ -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. + +# List certificate files, useful to distribute them to another host +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +root=`echo "import os; print os.path.realpath('$1')" | python` + +cd $root +find . -name "*.crt" +find . -name "*.pem" +find . -name "*.p12" +find . -name "*.key" +find . -name "*.0" + diff --git a/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf new file mode 100755 index 0000000000..021d205ed9 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf @@ -0,0 +1,55 @@ +#!/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 an SSL tunnel configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +port=`$here/httpd-addr port $2` +sslhost=$3 +sslport=$4 +tport=$5 + +# Generate HTTPD configuration +cat >>$root/conf/httpd.conf <<EOF +# Generated by: tunnel-ssl-conf $* +# Tunnel TCP/IP traffic over HTTPS + +# Listen on local port +Listen 127.0.0.1:$port + +# Tunnel virtual host +<VirtualHost 127.0.0.1:$port> +ServerName http://localhost:$port + +TunnelPass https://$sslhost:$sslport/tunnel/localhost/$tport + +# Declare SSL certificates used in this virtual host +#TunnelSSLCACertificateFile "$root/cert/ca.crt" +TunnelSSLCertificateFile "$root/cert/tunnel.crt" +TunnelSSLCertificateKeyFile "$root/cert/tunnel.key" + +</VirtualHost> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am new file mode 100644 index 0000000000..8b80276d15 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am @@ -0,0 +1,70 @@ +# 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 + +if WANT_JAVA + +INCLUDES = -I${JAVA_INCLUDE} + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/java + +dist_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 + +EXTRA_DIST = domain-test.composite + +mod_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${libsuffix} +libmod_tuscany_java${libsuffix}: + ln -s .libs/libmod_tuscany_java${libsuffix} + +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} + +dist_mod_JAVA = org/apache/tuscany/*.java test/*.java +jardir = ${prefix}/modules/java +jarfile = libmod-tuscany-java-${PACKAGE_VERSION}.jar +jar_DATA = ${jarfile} +${jarfile}: ${dist_mod_JAVA} + ${JAR} cf $@ org/apache/tuscany/*.class + +CLEANFILES = *.stamp ${jarfile} org/apache/tuscany/*.class test/*.class + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +dist_noinst_SCRIPTS = server-test wiring-test +noinst_PROGRAMS = jni-test java-test client-test +mod_PROGRAMS = java-shell +TESTS = jni-test java-test server-test + +endif diff --git a/sca-cpp/branches/lightweight-sca/modules/java/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/client-test.cpp new file mode 100644 index 0000000000..d06c57721e --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite new file mode 100644 index 0000000000..a4c696c38d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite @@ -0,0 +1,41 @@ +<?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" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="java-test"> + <implementation.java class="test.ServerImpl"/> + <service name="test"> + <binding.http uri="java"/> + </service> + </component> + + <component name="client-test"> + <implementation.java class="test.ClientImpl"/> + <service name="client"> + <binding.http uri="client"/> + </service> + <reference name="ref" target="java-test"> + <binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/lightweight-sca/modules/java/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/java/driver.hpp new file mode 100644 index 0000000000..ddfc057940 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/java/eval.hpp new file mode 100644 index 0000000000..31fb09f741 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/eval.hpp @@ -0,0 +1,566 @@ +/* + * 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 "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() { + debug("java::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_ASSERT + // 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); + } + + ~JavaRuntime() { + } + + 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) { + const value uuid = mkuuid(); + return env->NewStringUTF(c_str(uuid)); +} + +/** + * 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((jsize)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 size_t 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/lightweight-sca/modules/java/java-conf b/sca-cpp/branches/lightweight-sca/modules/java/java-conf new file mode 100755 index 0000000000..baa5c059c2 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/java-conf @@ -0,0 +1,38 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +cat >>$root/conf/modules.conf <<EOF +# Generated by: java-conf $* +# Support for Java SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_java${libsuffix} + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp new file mode 100644 index 0000000000..51df513990 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/java-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/java-test.cpp new file mode 100644 index 0000000000..f811a4f58d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/jni-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/jni-test.cpp new file mode 100644 index 0000000000..727af13dc6 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/mod-java.cpp b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp new file mode 100644 index 0000000000..d96861cc6d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval Java component implementations. + */ + +#define WANT_HTTPD_LOG 1 +#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); + if (contains(itype, ".widget")) + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype, -1, false); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp new file mode 100644 index 0000000000..b68f17aa3f --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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), rcode(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>&)> >(jc); + return lambda<value(const list<value>&)>(applyImplementation(content(jc), px, jr)); +} + +} +} +} + +#endif /* tuscany_modjava_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java new file mode 100644 index 0000000000..ef7b2316fb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java new file mode 100644 index 0000000000..06466fe9fc --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java new file mode 100644 index 0000000000..2366d79af6 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java @@ -0,0 +1,398 @@ +/* + * 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 cdr of the cdr of the cdr of a list. + */ + public static <T> Iterable<T> cdddr(final Object l) { + return cdr(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 car of the cdr of the cdr of the cdr of a list. + */ + @SuppressWarnings("unchecked") + public static <T> T cadddr(final Object l) { + return (T)car(cdddr(l)); + } + + /** + * Appends a list and another list. + */ + @SuppressWarnings("unchecked") + public static <T> Iterable<T> append(final Object a, final Object b) { + if (isNil(a)) + return (Iterable<T>)b; + return cons(car(a), append(cdr(a), b)); + } + + /** + * 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); + + final Iterable<Object> a = list(0, 1, 2); + final Iterable<Object> b = list(3, 4); + final Iterable<Object> ab = append(a, b); + assert ab.toString().equals("[0, 1, 2, 3, 4]"); + 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/lightweight-sca/modules/java/org/apache/tuscany/Service.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/Service.java new file mode 100644 index 0000000000..a00d5b1b53 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java new file mode 100644 index 0000000000..60076c62ca --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/server-test b/sca-cpp/branches/lightweight-sca/modules/java/server-test new file mode 100755 index 0000000000..0b45649ace --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/server-test @@ -0,0 +1,42 @@ +#!/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 +rm -rf tmp +../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 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java new file mode 100644 index 0000000000..7236548c41 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/AdderImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/AdderImpl.java new file mode 100644 index 0000000000..e607012b78 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/CalcImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/CalcImpl.java new file mode 100644 index 0000000000..5bea01a43f --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/Client.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Client.java new file mode 100644 index 0000000000..c3bd875fcc --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/ClientImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/ClientImpl.java new file mode 100644 index 0000000000..ade2ba302e --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/Server.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Server.java new file mode 100644 index 0000000000..3dfe3c84ef --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/java/test/ServerImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java new file mode 100644 index 0000000000..ee25cf7bf8 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java @@ -0,0 +1,55 @@ +/* + * 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(list("'feed", list("'title", "Sample Feed"), list("'id", "123456789"), list("'entry", list( + list(list("'title", "Item"), list("'id", "111"), + list("'content", list("'item", list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99)))), + list(list("'title", "Item"), list("'id", "222"), + list("'content", list("'item", list("'name", "Orange"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 3.55)))), + list(list("'title", "Item"), list("'id", "333"), + list("'content", list("'item", list("'name", "Pear"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 1.55)))))))); + + final Iterable<?> content = list("'content", list("'item", list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99))); + return list(list("'entry", list("'title", "Item"), list("'id", car(id)), content)); + } + + 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/lightweight-sca/modules/java/wiring-test b/sca-cpp/branches/lightweight-sca/modules/java/wiring-test new file mode 100755 index 0000000000..dd865c4c66 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/java/wiring-test @@ -0,0 +1,81 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +rm -rf tmp +../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/test/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/test/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/test/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/test/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/test/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt ../server/htdocs/test/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am new file mode 100644 index 0000000000..38627678a1 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am @@ -0,0 +1,56 @@ +# 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. + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/js + +jsfiles = htdocs/util.js htdocs/elemutil.js htdocs/xmlutil.js htdocs/atomutil.js htdocs/jsonutil.js htdocs/scdl.js htdocs/ui.js htdocs/component.js + +BUILT_SOURCES = htdocs/all.js +htdocs/all.js: ${jsfiles} + cat $^ >htdocs/all.js + +minified = htdocs/all-min.js htdocs/ui-min.css + +SUFFIXES = -min.html -min.js -min.css +.html-min.html: + ../../modules/http/minify-html $< $@ + +.js-min.js: + ../../modules/http/minify-js $< $@ + +.css-min.css: + ../../modules/http/minify-css $< $@ + +CLEANFILES = htdocs/all.js ${minified} + +dist_mod_SCRIPTS = js-conf +moddir = $(prefix)/modules/js +nobase_dist_mod_DATA = ${minified} +EXTRA_DIST = ${jsfiles} htdocs/ui.css + +js_test_SOURCES = js-test.cpp +js_test_LDFLAGS = -lmozjs + +js_shell_SOURCES = js-shell.cpp +js_shell_LDFLAGS = -lmozjs + +noinst_PROGRAMS = js-test +mod_PROGRAMS = js-shell +dist_noinst_SCRIPTS = util-test +TESTS = js-test util-test + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp new file mode 100644 index 0000000000..f8f4cbe598 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp @@ -0,0 +1,365 @@ +/* + * 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_js_hpp +#define tuscany_js_hpp + +/** + * Javascript evaluation functions. + */ + +#define XP_UNIX +#ifdef WANT_MAINTAINER_WARNINGS +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif +#include <jsapi.h> +#ifdef WANT_MAINTAINER_WARNINGS +#pragma GCC diagnostic warning "-Wunused-parameter" +#pragma GCC diagnostic warning "-Wsign-compare" +#pragma GCC diagnostic warning "-Wredundant-decls" +#endif +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "parallel.hpp" + +namespace tuscany { +namespace js { + +/** + * Report Javascript errors. + */ +void reportError(unused ::JSContext *cx, const char *message, JSErrorReport *report) { + cfailure << (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 JSRuntime { +public: + JSRuntime() { + // Create JS runtime + debug("js::jsruntime"); + rt = JS_NewRuntime(1L * 512L * 1024L); + if(rt == NULL) + cleanup(); + } + + operator ::JSRuntime*() const { + return rt; + } + + ~JSRuntime() { + debug("js::~jsruntime"); + } + +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_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS }; + +/** + * Represents a JavaScript context. Maintains one context per thread. + */ +#ifdef WANT_THREADS +perthread_ptr<JSContext> jsContext; +#else +::JSContext* jsContext = NULL; +#endif + +class JSContext { +public: + JSContext() { + // Create JS context if necessary + debug("js::jscontext"); + if (jsContext != NULL) { + cx = jsContext; + JS_BeginRequest(cx); + return; + } + debug("js::jsnewcontext"); + cx = JS_NewContext(jsRuntime, 8192); + if(cx == NULL) + return; + JS_BeginRequest(cx); + + JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT); + JS_SetVersion(cx, JSVERSION_LATEST); + JS_SetErrorReporter(cx, reportError); + //JS_SetGCZeal(cx, 2); + + // Create global JS object + global = JS_NewCompartmentAndGlobalObject(cx, &jsGlobalClass, NULL); + if(global == NULL) { + cleanup(); + return; + } + + // Populate global object with the standard globals, like Object and Array + if(!JS_InitStandardClasses(cx, global)) { + cleanup(); + return; + } + jsContext = cx; + } + + ~JSContext() { + debug("js::~jscontext"); + cleanup(); + } + + operator ::JSContext*() const { + return cx; + } + + JSObject* getGlobal() const { + return global; + } + +private: + bool cleanup() { + if(cx != NULL) { + JS_MaybeGC(cx); + JS_EndRequest(cx); + if (cx != jsContext) { + debug("js::jsdestroycontext"); + 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 js::JSContext& cx) { + + const value jsValToValue(const jsval& jsv, const js::JSContext& cx); + + jsid id; + if(!JS_NextProperty(cx, i, &id)) + return propertiesSoFar; + jsval idv; + JS_IdToValue(cx, id, &idv); + if (idv == JSVAL_VOID) + return propertiesSoFar; + + jsval jsv; + if(!JS_GetPropertyById(cx, o, id, &jsv)) + return propertiesSoFar; + const value val = jsValToValue(jsv, cx); + + if(JSVAL_IS_STRING(idv)) { + char* cname = JS_EncodeString(cx, JSVAL_TO_STRING(idv)); + const string name = cname; + JS_free(cx, cname); + if (isNil(val) && !isList(val)) + return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, i, cx); + //return jsPropertiesToValues(propertiesSoFar, o, i, cx); + 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 js::JSContext& cx) { + switch(JS_TypeOfValue(cx, jsv)) { + case JSTYPE_STRING: { + char* cvalue = JS_EncodeString(cx, JSVAL_TO_STRING(jsv)); + const string svalue = string(cvalue); + JS_free(cx, cvalue); + return value(svalue); + } + 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); + if (o == NULL) + return value(); + 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(); + } + } +} + +/** + * Converts a list of values to JS array elements. + */ +JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const js::JSContext& cx) { + const jsval valueToJSVal(const value& val, const js::JSContext& 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 js::JSContext& cx) { + JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const js::JSContext& cx); + + switch(type(val)) { + case value::String: { + return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c_str((string)val))); + } + 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: { + jsval jsv; + if (!JS_NewNumberValue(cx, (jsdouble)val, &jsv)) + return DOUBLE_TO_JSVAL(0); + return jsv; + } + 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)); + } + case value::Nil: { + return JSVAL_NULL; + } + default: { + return JSVAL_VOID; + } + } +} + +/** + * Converts a list of values to JS properties. + */ +JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const js::JSContext& 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); +} + +/** + * Evaluate a script provided as a string. + */ +const failable<value> evalScript(const string& s) { + js::JSContext cx; + jsval rval; + JSBool rc = JS_EvaluateScript(cx, cx.getGlobal(), c_str(s), (uintN)length(s), "eval.js", 1, &rval); + if (rc != JS_TRUE) { + return mkfailure<value>("Couldn't evaluate Javascript script."); + } + return jsValToValue(rval, cx); +} + +} +} + +#endif /* tuscany_js_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js new file mode 100644 index 0000000000..068b5de2fd --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js @@ -0,0 +1,184 @@ +/* + * 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. + */ +var atom = {}; + +/** + * Convert a list of elements to a list of values representing an ATOM entry. + */ +atom.entryElementValues = function(e) { + var lt = filter(selector(mklist(element, "'title")), e); + var t = mklist(element, "'title", isNil(lt)? '' : elementValue(car(lt))); + + var li = filter(selector(mklist(element, "'id")), e); + var i = mklist(element, "'id", isNil(li)? '' : elementValue(car(li))); + + var la = filter(selector(mklist(element, "'author")), e); + var lan = isNil(la)? mklist() : filter(selector(mklist(element, "'name")), car(la)); + var lae = isNil(la)? mklist() : filter(selector(mklist(element, "'email")), car(la)); + var laa = isNil(lan)? lae : lan; + var a = isNil(laa)? mklist() : mklist(mklist(element, "'author", elementValue(car(laa)))); + + var lu = filter(selector(mklist(element, "'updated")), e); + var u = isNil(lu)? mklist() : mklist(mklist(element, "'updated", elementValue(car(lu)))); + + var lc = filter(selector(mklist(element, "'content")), e); + var c = isNil(lc)? mklist() : mklist(mklist(element, "'content", elementValue(car(lc)))); + + return append(append(append(mklist(element, "'entry", t, i), a), u), c); +}; + +/** + * Convert a list of elements to a list of values representing ATOM entries. + */ +atom.entriesElementValues = function(e) { + if (isNil(e)) + return e; + return cons(atom.entryElementValues(car(e)), atom.entriesElementValues(cdr(e))); +}; + +/** + * Return true if a list of strings represents an ATOM entry. + */ +atom.isATOMEntry = function(l) { + if (!isXML(l)) + return false; + return car(l).match('<entry') != null && car(l).match('<feed') == null && car(l).match('="http://www.w3.org/2005/Atom"') != null; +}; + +/** + * Convert a DOM Document to a list of values representing an ATOM entry. + */ +atom.readATOMEntryDocument = function(doc) { + var e = readXMLDocument(doc); + if (isNil(e)) + return mklist(); + return mklist(atom.entryElementValues(car(e))); +}; + +/** + * Convert a list of strings to a list of values representing an ATOM entry. + */ +atom.readATOMEntry = function(l) { + return atom.readATOMEntryDocument(parseXML(l)); +}; + +/** + * Return true if a list of strings represents an ATOM feed. + */ +atom.isATOMFeed = function(l) { + if (!isXML(l)) + return false; + return car(l).match('<feed') != null && car(l).match('="http://www.w3.org/2005/Atom"') != null; +}; + +/** + * Convert a DOM document to a list of values representing an ATOM feed. + */ +atom.readATOMFeedDocument = function(doc) { + var f = readXMLDocument(doc); + if (isNil(f)) + return mklist(); + var t = filter(selector(mklist(element, "'title")), car(f)); + var i = filter(selector(mklist(element, "'id")), car(f)); + var e = filter(selector(mklist(element, "'entry")), car(f)); + return mklist(append( + mklist(element, "'feed", mklist(element, "'title", elementValue(car(t))), mklist(element, "'id", elementValue(car(i)))), + atom.entriesElementValues(e))); +}; + +/** + * Convert a list of strings to a list of values representing an ATOM feed. + */ +atom.readATOMFeed = function(l) { + return atom.readATOMFeedDocument(parseXML(l)); +}; + +/** + * Convert a list of values representy an ATOM entry to a list of elements. + */ +atom.entryElement = function(l) { + var title = elementValue(namedElementChild("'title", l)); + var id = elementValue(namedElementChild("'id", l)); + var author = namedElementChild("'author", l); + var email = isNil(author)? false : (elementValue(author).indexOf('@') != -1); + var updated = namedElementChild("'updated", l); + var content = namedElementChild("'content", l); + var text = isNil(content)? false : elementHasValue(content); + return append(append(append(append( + mklist(element, "'entry", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + mklist(element, "'title", mklist(attribute, "'type", "text"), title), mklist(element, "'id", id)), + isNil(author)? mklist() : mklist(element, "'author", + (email? mklist(element, "'email", elementValue(author)) : mklist(element, "'name", elementValue(author))))), + isNil(updated)? mklist() : mklist(element, "'updated", elementValue(updated))), + isNil(content)? mklist() : + mklist(append(mklist(element, "'content", mklist(attribute, "'type", text? "text" : "application/xml")), + text? mklist(elementValue(content)) : elementChildren(content)))), + mklist(mklist(element, "'link", mklist(attribute, "'href", id)))); +}; + +/** + * Convert a list of values representing ATOM entries to a list of elements. + */ +atom.entriesElements = function(l) { + if (isNil(l)) + return l; + return cons(atom.entryElement(car(l)), atom.entriesElements(cdr(l))); +}; + +/** + * Convert a list of values representing an ATOM entry to an ATOM entry. + */ +atom.writeATOMEntry = function(ll) { + var l = isNil(ll)? ll : car(ll); + return writeXML(mklist(atom.entryElement(l)), true); +}; + +/** + * Convert a list of values representing an ATOM feed to an ATOM feed. + */ +atom.writeATOMFeed = function(ll) { + var l = isNil(ll)? ll : car(ll); + var lt = filter(selector(mklist(element, "'title")), l); + var t = isNil(lt)? '' : elementValue(car(lt)); + var li = filter(selector(mklist(element, "'id")), l); + var i = isNil(li)? '' : elementValue(car(li)); + var f = mklist(element, "'feed", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + mklist(element, "'title", mklist(attribute, "'type", "text"), car(l)), + mklist(element, "'id", cadr(l))); + + // Write ATOM entries + var le = filter(selector(mklist(element, "'entry")), l); + if (isNil(le)) + return writeXML(mklist(f), true); + + // Write a single ATOM entry element with a list of values + if (!isNil(le) && !isNil(car(le)) && isList(car(caddr(car(le))))) { + var fe = append(f, atom.entriesElements(caddr(car(le)))); + return writeXML(mklist(fe), true); + } + + // Write separate ATOM entry elements + var fe = append(f, atom.entriesElements(le)); + return writeXML(mklist(fe), true); +}; + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js new file mode 100644 index 0000000000..c3799ef708 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js @@ -0,0 +1,637 @@ +/* + * 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. + * + * The JSON-RPC client code is based on Jan-Klaas' JavaScript + * o lait library (jsolait). + * + * $Id: jsonrpc.js,v 1.36.2.3 2006/03/08 15:09:37 mclark Exp $ + * + * Copyright (c) 2003-2004 Jan-Klaas Kollhof + * Copyright (c) 2005 Michael Clark, Metaparadigm Pte Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"). + */ + +/** + * Client component wiring API, supporting JSON and ATOM bindings. + */ + +var JSONClient = {}; + +/** + * Escape a character. + */ +JSONClient.escapeJSONChar = function(c) { + if(c == "\"" || c == "\\") return "\\" + c; + if (c == "\b") return "\\b"; + if (c == "\f") return "\\f"; + if (c == "\n") return "\\n"; + if (c == "\r") return "\\r"; + if (c == "\t") return "\\t"; + var hex = c.charCodeAt(0).toString(16); + if(hex.length == 1) return "\\u000" + hex; + if(hex.length == 2) return "\\u00" + hex; + if(hex.length == 3) return "\\u0" + hex; + return "\\u" + hex; +}; + +/** + * Encode a string into JSON format. + */ +JSONClient.escapeJSONString = function(s) { + // The following should suffice but Safari's regex is broken (doesn't support callback substitutions) + // return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, JSONClient.escapeJSONChar) + "\""; + + // Rather inefficient way to do it + var parts = s.split(""); + for(var i = 0; i < parts.length; i++) { + var c = parts[i]; + if(c == '"' || c == '\\' || c.charCodeAt(0) < 32 || c.charCodeAt(0) >= 128) + parts[i] = JSONClient.escapeJSONChar(parts[i]); + } + return "\"" + parts.join("") + "\""; +}; + +/** + * Marshall objects to JSON format. + */ +JSONClient.toJSON = function(o) { + if(o == null) + return "null"; + if(o.constructor == String) + return JSONClient.escapeJSONString(o); + if(o.constructor == Number) + return o.toString(); + if(o.constructor == Boolean) + return o.toString(); + if(o.constructor == Date) + return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}'; + if(o.constructor == Array) { + var v = []; + for(var i = 0; i < o.length; i++) + v.push(JSONClient.toJSON(o[i])); + return "[" + v.join(", ") + "]"; + } + var v = []; + for(attr in o) { + if(o[attr] == null) + v.push("\"" + attr + "\": null"); + else if(typeof o[attr] == "function") + ; // Skip + else + v.push(JSONClient.escapeJSONString(attr) + ": " + JSONClient.toJSON(o[attr])); + } + return "{" + v.join(", ") + "}"; +}; + +/** + * Construct an HTTPBindingClient. + */ +function HTTPBindingClient(name, uri, domain) { + this.name = name; + this.domain = domain; + this.uri = uri; + this.apply = this.createApplyMethod(); +} + +/** + * JSON-RPC request counter. + */ +HTTPBindingClient.jsonrpcID = 1; + +/** + * HTTPBindingClient implementation + */ + +/** + * Generate client proxy apply method. + */ +HTTPBindingClient.prototype.createApplyMethod = function() { + var fn = function() { + var methodName = arguments[0]; + var args = []; + for(var i = 1; i < arguments.length; i++) + args.push(arguments[i]); + + var cb = null; + if (typeof args[args.length - 1] == "function") + cb = args.pop(); + + var req = HTTPBindingClient.makeJSONRequest(methodName, args, cb); + return fn.client.jsonApply(req); + }; + fn.client = this; + return fn; +}; + +/** + * Make a JSON-RPC request. + */ +HTTPBindingClient.makeJSONRequest = function(methodName, args, cb) { + var req = {}; + req.id = HTTPBindingClient.jsonrpcID++; + if (cb) + req.cb = cb; + var obj = {}; + obj.id = req.id; + obj.method = methodName; + obj.params = args; + req.data = JSONClient.toJSON(obj); + return req; +}; + +/** + * Return the JSON result from an XMLHttpRequest. + */ +HTTPBindingClient.jsonResult = function(http) { + // Get the charset + function httpCharset(http) { + try { + var contentType = http.getResponseHeader("Content-Type"); + var parts = contentType.split(/\s*;\s*/); + for (var i = 0; i < parts.length; i++) { + if (parts[i].substring(0, 8) == "charset=") + return parts[i].substring(8, parts[i].length); + } + } catch (e) {} + return "UTF-8"; + } + if(!HTTPBindingClient.charset) + HTTPBindingClient.charset = httpCharset(http); + + // Unmarshall the JSON response + var obj; + eval("obj = " + http.responseText); + if(obj.error) + throw new HTTPBindingClient.Exception(obj.error.code, obj.error.msg); + var res = obj.result; + return res; +}; + +/** + * Apply a function remotely using JSON-RPC. + */ +HTTPBindingClient.prototype.jsonApply = function(req) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = req.cb? true : false; + http.open("POST", this.uri, hascb); + http.setRequestHeader("Accept", "*/*"); + http.setRequestHeader("Content-Type", "application/json-rpc"); + + // Construct call back if we have one + if(hascb) { + http.onreadystatechange = function() { + if(http.readyState == 4) { + // Pass the result or exception + if(http.status == 200) { + var res = null; + try { + res = HTTPBindingClient.jsonResult(http); + try { + req.cb(res); + } catch(cbe) {} + } catch(e) { + try { + req.cb(null, e); + } catch(cbe) {} + } + } else + try { + req.cb(null, HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + }; + + // Send the request + http.send(req.data); + return req.id; + } + + // Send the request and return the result or exception + http.send(req.data); + if (http.status == 200) + return HTTPBindingClient.jsonResult(http); + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + + +/** + * REST GET method. + */ +HTTPBindingClient.prototype.get = function(id, cb) { + var u = id? (this.uri? this.uri + '/' + id : id) : this.uri; + var hascb = cb? true : false; + + // Get from local storage first + var ls = window.lstorage || localStorage; + var item = null; + try { item = ls.getItem(u); } catch(e) {} + //log('localStorage.getItem', u, item); + if (item != null && item != '') { + if (!hascb) + return item; + + // Pass local result to callback + try { + cb(item); + } catch (cbe) {} + } + + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + http.open("GET", u, hascb); + http.setRequestHeader("Accept", "*/*"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + //log('readystate', http.readyState, 'status', http.status, 'headers', http.getAllResponseHeaders()); + if (http.readyState == 4) { + // Pass result if different from local result + if (http.status == 200) { + + if (http.getResponseHeader("X-Login") != null) { + // Detect redirect to a login page + try { + var le = new HTTPBindingClient.Exception(403, 'X-Login'); + if (window.onloginredirect) + window.onloginredirect(le); + return cb(null, le); + } catch(cbe) {} + + } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) { + // Report empty response + try { + return cb(null, new HTTPBindingClient.Exception(403, 'No-Content')); + } catch(cbe) {} + + } else { + if (item == null || http.responseText != item) { + // Store retrieved entry in local storage + if (http.responseText != null) { + //log('localStorage.setItem', u, http.responseText); + try { ls.setItem(u, http.responseText); } catch(e) {} + } + try { + return cb(http.responseText); + } catch(cbe) {} + } + } + } + else { + // Pass exception if we didn't have a local result + if (item == null) { + try { + return cb(null, new HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + } + } + }; + + // Send the request + http.send(null); + return true; + } + + // Send the request and return the result or exception + http.send(null); + if (http.status == 200) { + if (http.getResponseHeader("X-Login") != null) { + + // Detect redirect to a login page + var le = new HTTPBindingClient.Exception(403, 'X-Login'); + if (window.onloginredirect) + window.onloginredirect(le); + throw le; + + } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) { + + // Report empty response + throw new HTTPBindingClient.Exception(403, 'No-Content'); + } + return http.responseText; + } + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST GET method, does not use the local cache. + */ +HTTPBindingClient.prototype.getnocache = function(id, cb) { + var u = id? (this.uri? this.uri + '/' + id : id) : this.uri; + var hascb = cb? true : false; + + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + http.open("GET", u, hascb); + http.setRequestHeader("Accept", "*/*"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + if (http.status == 200) { + + if (http.getResponseHeader("X-Login") != null) { + // Detect redirect to a login page + try { + var le = new HTTPBindingClient.Exception(403, 'X-Login'); + if (window.onloginredirect) + window.onloginredirect(le); + return cb(null, le); + } catch(cbe) {} + + } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) { + // Report empty response + try { + return cb(null, new HTTPBindingClient.Exception(403, 'No-Content')); + } catch(cbe) {} + + } else { + try { + return cb(http.responseText); + } catch(cbe) {} + } + } else { + try { + return cb(null, new HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + } + }; + + // Send the request + http.send(null); + return true; + } + + // Send the request and return the result or exception + http.send(null); + if (http.status == 200) { + if (http.getResponseHeader("X-Login") != null) { + + // Detect redirect to a login page + var le = new HTTPBindingClient.Exception(403, 'X-Login'); + if (window.onloginredirect) + window.onloginredirect(le); + throw le; + + } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) { + + // Report empty response + throw new HTTPBindingClient.Exception(403, 'No-Content'); + } + return http.responseText; + } + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST POST method. + */ +HTTPBindingClient.prototype.post = function (entry, cb) { + + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("POST", this.uri, hascb); + http.setRequestHeader("Accept", "*/*"); + http.setRequestHeader("Content-Type", "application/atom+xml"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + // Pass the result or exception + if (http.readyState == 4) { + if (http.status == 201) { + try { + cb(http.responseText); + } catch(cbe) {} + } + else { + try { + cb(null, new HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + } + }; + // Send the request + http.send(entry); + return true; + } + + // Send the request and return the result or exception + http.send(entry); + if (http.status == 201) + return http.responseText; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST PUT method. + */ +HTTPBindingClient.prototype.put = function (id, entry, cb) { + var u = this.uri + '/' + id; + + // Update local storage + var ls = window.lstorage || localStorage; + try { ls.setItem(u, entry); } catch(e) {} + //log('localStorage.setItem', u, entry); + + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("PUT", u, hascb); + http.setRequestHeader("Accept", "*/*"); + http.setRequestHeader("Content-Type", "application/atom+xml"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + // Pass any exception + if (http.status == 200) { + try { + cb(); + } catch(cbe) {} + } else { + try { + cb(new HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + } + }; + // Send the request + http.send(entry); + return true; + } + + // Send the request and return any exception + http.send(entry); + if (http.status == 200) + return true; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST DELETE method. + */ +HTTPBindingClient.prototype.del = function (id, cb) { + var u = this.uri + '/' + id; + + // Update local storage + var ls = window.lstorage || localStorage; + try { ls.removeItem(u); } catch(e) {} + //log('localStorage.removeItem', u); + + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("DELETE", u, hascb); + http.setRequestHeader("Accept", "*/*"); + + // Construct call back if we have one + if (cb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + // Pass any exception + if (http.status == 200) { + try { + cb(); + } catch(cbe) {} + } + else { + try { + cb(new HTTPBindingClient.Exception(http.status, http.statusText)); + } catch(cbe) {} + } + } + }; + // Send the request + http.send(null); + return true; + } + + // Send the request and return any exception + http.send(null); + if (http.status == 200) + return true; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * HTTPBindingClient exceptions. + */ +HTTPBindingClient.Exception = function(code, message) { + this.name = "HTTPBindingClientException"; + this.code = code; + this.message = message; +}; + +HTTPBindingClient.Exception.prototype = new Error(); + +HTTPBindingClient.Exception.prototype.toString = function() { + return this.name + ": " + this.message; +}; + +/** + * XMLHttpRequest wrapper. + */ +HTTPBindingClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ]; + +HTTPBindingClient.getHTTPRequest = function() { + if (HTTPBindingClient.httpFactory) + return HTTPBindingClient.httpFactory(); + + // Mozilla XMLHttpRequest + try { + HTTPBindingClient.httpFactory = function() { + return new XMLHttpRequest(); + }; + return HTTPBindingClient.httpFactory(); + } catch(e) {} + + // Microsoft MSXML ActiveX + for (var i = 0; i < HTTPBindingClient.msxmlNames.length; i++) { + try { + HTTPBindingClient.httpFactory = function() { + return new ActiveXObject(HTTPBindingClient.msxmlNames[i]); + }; + return HTTPBindingClient.httpFactory(); + } catch (e) {} + } + + // Can't create XMLHttpRequest + HTTPBindingClient.httpFactory = null; + throw new HTTPBindingClient.Exception(0, "Can't create XMLHttpRequest object"); +}; + +/** + * Public API. + */ + +var sca = {}; + +/** + * Return an HTTP client proxy. + */ +sca.httpclient = function(name, uri, domain) { + return new HTTPBindingClient(name, uri, domain); +}; + +/** + * Return a component proxy. + */ +sca.component = function(name, domain) { + if (!domain) + return new HTTPBindingClient(name, '/c/' + name, domain); + return new HTTPBindingClient(name, '/' + domain + '/c/' + name, domain); +}; + +/** + * Return a reference proxy. + */ +sca.reference = function(comp, rname) { + if (!comp.domain) + return new HTTPBindingClient(comp.name + '/' + rname, '/r/' + comp.name + '/' + rname, comp.domain); + return new HTTPBindingClient(comp.name + '/' + rname, '/' + comp.domain + '/r/' + comp.name + '/' + rname, comp.domain); +}; + +/** + * Add proxy functions to a reference proxy. + */ +sca.defun = function(ref) { + function defapply(name) { + return function() { + var args = new Array(); + args[0] = name; + for (i = 0, n = arguments.length; i < n; i++) + args[i + 1] = arguments[i]; + return this.apply.apply(this, args); + }; + } + + for (f = 1; f < arguments.length; f++) { + var fn = arguments[f]; + ref[fn]= defapply(fn); + } + return ref; +}; + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js new file mode 100644 index 0000000000..37d641f7b3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js @@ -0,0 +1,236 @@ +/* + * 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. + */ + +var element = "'element" +var attribute = "'attribute" +var atsign = "'@" + +/** + * Return true if a value is an element. + */ +function isElement(v) { + return (!(!isList(v) || isNil(v) || car(v) != element)); +} + +/** + * Return true if a value is an attribute. + */ +function isAttribute(v) { + return (!(!isList(v) || isNil(v) || car(v) != attribute)); +} + +/** + * Return the name of an attribute. + */ +attributeName = cadr; + +/** + * Return the value of an attribute. + */ +attributeValue = caddr; + +/** + * Return the name of an element. + */ +elementName = cadr; + +/** + * Return true if an element has children. + */ +function elementHasChildren(l) { + return !isNil(cddr(l)); +} + +/** + * Return the children of an element. + */ +elementChildren = cddr; + +/** + * Return true if an element has a value. + */ +function elementHasValue(l) { + var r = reverse(l); + if (isSymbol(car(r))) + return false; + return (!(isList(car(r)) && !isNil(car(r)) && isSymbol(car(car(r))))) +} + +/** + * Return the value of an element. + */ +function elementValue(l) { + return car(reverse(l)); +} + +/** + * Convert an element to a value. + */ +function elementToValueIsList(v) { + if (!isList(v)) + return false; + return isNil(v) || !isSymbol(car(v)); +} + +function elementToValue(t) { + if (isTaggedList(t, attribute)) + return mklist(atsign + attributeName(t).substring(1), attributeValue(t)); + if (isTaggedList(t, element)) { + if (elementHasValue(t)) { + if (!elementToValueIsList(elementValue(t))) + return mklist(elementName(t), elementValue(t)); + return cons(elementName(t), mklist(elementsToValues(elementValue(t)))); + } + return cons(elementName(t), elementsToValues(elementChildren(t))); + } + if (!isList(t)) + return t; + return elementsToValues(t); +} + +/** + * Convert a list of elements to a list of values. + */ +function elementToValueIsSymbol(v) { + return (!(!isList(v)) || isNil(v) || !isSymbol(car(v))); +} + +function elementToValueGroupValues(v, l) { + if (isNil(l) || !elementToValueIsSymbol(v) || !elementToValueIsSymbol(car(l))) + return cons(v, l); + if (car(car(l)) != car(v)) + return cons(v, l); + if (!elementToValueIsList(cadr(car(l)))) { + var g = mklist(car(v), mklist(cdr(v), cdr(car(l)))); + return elementToValueGroupValues(g, cdr(l)); + } + var g = mklist(car(v), cons(cdr(v), cadr(car(l)))); + return elementToValueGroupValues(g, cdr(l)); +} + +function elementsToValues(e) { + if (isNil(e)) + return e; + return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e))); +} + +/** + * Convert a value to an element. + */ +function valueToElement(t) { + if (isList(t) && !isNil(t) && isSymbol(car(t))) { + var n = car(t); + var v = isNil(cdr(t))? mklist() : cadr(t); + if (!isList(v)) { + if (n.substring(0, 2) == atsign) + return mklist(attribute, "'" + n.substring(2), v); + return mklist(element, n, v); + } + if (isNil(v) || !isSymbol(car(v))) + return cons(element, cons(n, mklist(valuesToElements(v)))); + return cons(element, cons(n, valuesToElements(cdr(t)))); + } + if (!isList(t)) + return t; + return valuesToElements(t); +} + +/** + * Convert a list of values to a list of elements. + */ +function 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. + */ +function selector(s) { + function 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)); + } + + return function(v) { return evalSelect(s, v); }; +} + +/** + * Return the attribute with the given name. + */ +function namedAttribute(name, l) { + return memo(l, name, function() { + var f = filter(function(v) { return isAttribute(v) && attributeName(v) == name; }, l); + if (isNil(f)) + return null; + return car(f); + }); +} + +/** + * Return the value of the attribute with the given name. + */ +function namedAttributeValue(name, l) { + var a = namedAttribute(name, l); + if (a == null) + return null + return attributeValue(a); +} + +/** + * Return child elements with the given name. + */ +function namedElementChildren(name, l) { + return memo(l, name, function() { + return filter(function(v) { return isElement(v) && elementName(v) == name; }, l); + }); +} + +/** + * Return the child element with the given name. + */ +function namedElementChild(name, l) { + var f = namedElementChildren(name, l); + if (isNil(f)) + return null; + return car(f); +} + +/** + * Side effect functions. Use with moderation. + */ + +/** + * Set the contents of an element. + */ +function setElement(l, e) { + setlist(l, e); + l.memo = {}; +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js new file mode 100644 index 0000000000..8aa291bc89 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js @@ -0,0 +1,261 @@ +/* + * 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. + */ +var json = {}; + +/** + * JSON exceptions. + */ +json.Exception = function(code, message) { + this.name = "JSONException"; + this.code = code; + this.message = message; +}; + +json.Exception.prototype = new Error(); + +json.Exception.prototype.toString = function() { + return this.name + ": " + this.message; +}; + +/** + * Return true if a list represents a JS array. + */ +json.isJSArray = function(l) { + if (isNil(l)) + return true; + var v = car(l); + if (isSymbol(v)) + return false; + if (isList(v)) + if (!isNil(v) && isSymbol(car(v))) + return false; + return true; +}; + +/** + * Converts JSON properties to values. + */ +json.jsPropertiesToValues = function(propertiesSoFar, o, i) { + if (isNil(i)) + return propertiesSoFar; + var p = car(i); + var jsv = o[p]; + var v = json.jsValToValue(jsv); + + if (typeof p == 'string') { + var n = '' + p; + if (n.slice(0, 1) == '@') + return json.jsPropertiesToValues(cons(mklist(attribute, "'" + n.slice(1), v), propertiesSoFar), o, cdr(i)); + if (isList(v) && !json.isJSArray(v)) + return json.jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i)); + return json.jsPropertiesToValues(cons(mklist(element, "'" + n, v), propertiesSoFar), o, cdr(i)); + } + return json.jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i)); +}; + +/** + * Converts a JSON val to a value. + */ +json.jsValToValue = function(jsv) { + if (jsv == null) + return null; + if (isList(jsv)) + return json.jsPropertiesToValues(mklist(), jsv, reverse(range(0, jsv.length))); + if (typeof jsv == 'object') + return json.jsPropertiesToValues(mklist(), jsv, reverse(properties(jsv))); + if (typeof jsv == 'string') + return '' + jsv; + return jsv; +} + +/** + * Return true if a list of strings contains a JSON document. + */ +json.isJSON = function(l) { + if (isNil(l)) + return false; + var s = car(l).slice(0, 1); + return s == "[" || s == "{"; +}; + +/** + * Convert a list of strings representing a JSON document to a list of values. + */ +json.readJSON = function(l) { + var s = writeStrings(l); + var obj; + eval('obj = { \"val\": ' + s + " }"); + return json.jsValToValue(obj.val); +}; + +/** + * Convert a list of values to JSON array elements. + */ +json.valuesToJSElements = function(a, l, i) { + if (isNil(l)) + return a; + var pv = json.valueToJSVal(car(l)); + a[i] = pv + return json.valuesToJSElements(a, cdr(l), i + 1); +}; + +/** + * Convert a value to a JSON value. + */ +json.valueToJSVal = function(v) { + if (!isList(v)) + return v; + if (json.isJSArray(v)) + return json.valuesToJSElements(range(0, v.length), v, 0); + return json.valuesToJSProperties({}, v); +}; + +/** + * Convert a list of values to JSON properties. + */ +json.valuesToJSProperties = function(o, l) { + if (isNil(l)) + return o; + var token = car(l); + if (isTaggedList(token, attribute)) { + var pv = json.valueToJSVal(attributeValue(token)); + o['@' + attributeName(token).slice(1)] = pv; + } else if (isTaggedList(token, element)) { + if (elementHasValue(token)) { + var pv = json.valueToJSVal(elementValue(token)); + o[elementName(token).slice(1)] = pv; + } else { + var child = {}; + o[elementName(token).slice(1)] = child; + json.valuesToJSProperties(child, elementChildren(token)); + } + } + return json.valuesToJSProperties(o, cdr(l)); +}; + +/** + * Convert a list of values to a list of strings representing a JSON document. + */ +json.writeJSON = function(l) { + var jsv; + if (json.isJSArray(l)) + jsv = json.valuesToJSElements(range(0, l.length), l, 0); + else + jsv = json.valuesToJSProperties({}, l); + var s = json.toJSON(jsv); + return mklist(s); +} + +/** + * Convert a list + params to a JSON-RPC request. + */ +json.jsonRequest = function(id, func, params) { + var r = mklist(mklist("'id", id), mklist("'method", func), mklist("'params", params)); + return json.writeJSON(valuesToElements(r)); +}; + +/** + * Convert a value to a JSON-RPC result. + */ +json.jsonResult = function(id, val) { + return json.writeJSON(valuesToElements(mklist(mklist("'id", id), mklist("'result", val)))); +}; + +/** + * Convert a JSON-RPC result to a value. + */ +json.jsonResultValue = function(s) { + var jsres = json.readJSON(s); + var res = elementsToValues(jsres); + var val = cadr(assoc("'result", res)); + if (isList(val) && !json.isJSArray(val)) + return mklist(val); + return val; +}; + +/** + * Escape a character. + */ +json.escapeJSONChar = function(c) { + if(c == "\"" || c == "\\") return "\\" + c; + if (c == "\b") return "\\b"; + if (c == "\f") return "\\f"; + if (c == "\n") return "\\n"; + if (c == "\r") return "\\r"; + if (c == "\t") return "\\t"; + var hex = c.charCodeAt(0).toString(16); + if(hex.length == 1) return "\\u000" + hex; + if(hex.length == 2) return "\\u00" + hex; + if(hex.length == 3) return "\\u0" + hex; + return "\\u" + hex; +}; + +/** + * Encode a string into JSON format. + */ +json.escapeJSONString = function(s) { + // The following should suffice but Safari's regex is broken (doesn't support callback substitutions) + // return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, json.escapeJSONChar) + "\""; + + // Rather inefficient way to do it + var parts = s.split(""); + for(var i = 0; i < parts.length; i++) { + var c = parts[i]; + if(c == '"' || c == '\\' || c.charCodeAt(0) < 32 || c.charCodeAt(0) >= 128) + parts[i] = json.escapeJSONChar(parts[i]); + } + return "\"" + parts.join("") + "\""; +}; + +/** + * Marshall objects to JSON format. + */ +json.toJSON = function(o) { + if(o == null) + return "null"; + if(o.constructor == String) + return json.escapeJSONString(o); + if(o.constructor == Number) + return o.toString(); + if(o.constructor == Boolean) + return o.toString(); + if(o.constructor == Date) + return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}'; + if(o.constructor == Array) { + var v = []; + for(var i = 0; i < o.length; i++) + v.push(json.toJSON(o[i])); + return "[" + v.join(", ") + "]"; + } + var v = []; + for(attr in o) { + if(o[attr] == null) + v.push("\"" + attr + "\": null"); + else if(typeof o[attr] == "function") + ; // Skip + else + v.push(json.escapeJSONString(attr) + ": " + json.toJSON(o[attr])); + } + return "{" + v.join(", ") + "}"; +}; + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js new file mode 100644 index 0000000000..50dab53e7c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js @@ -0,0 +1,250 @@ +/* + * 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. + */ +var scdl = {}; + +/** + * Returns a composite element. + */ +scdl.composite = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return car(cs); +}; + +/** + * Returns a list of components in a composite. + */ +scdl.components = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return namedElementChildren("'component", car(cs)); +}; + +/** + * Returns a list of service promotions in a composite. + */ +scdl.promotions = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return namedElementChildren("'service", car(cs)); +}; + +/** + * Returns the target of a service promotion. + */ +scdl.promote = function(l) { + var puri = namedAttributeValue("'promote", l); + if (isNil(puri)) + return puri; + return car(tokens(puri)); +}; + +/** + * Returns the name of a component, componentType, service or reference. + */ +scdl.name = function(l) { + return namedAttributeValue("'name", l); +}; + +/** + * Returns the description of a component, componentType, service or reference. + */ +scdl.documentation = function(l) { + var d = namedElementChildren("'documentation", l); + if (isNil(d)) + return null; + if (!elementHasValue(car(d))) + return null; + var v = elementValue(car(d)); + return v; +}; + +/** + * Returns the title of a component or componentType. + */ +scdl.title = function(l) { + return namedAttributeValue("'title", l); +}; + +/** + * Returns the display style of a component, componentType, reference or property. + */ +scdl.style = function(l) { + return namedAttributeValue("'style", l); +}; + +/** + * Returns the color of a component or componentType. + */ +scdl.color = function(l) { + return namedAttributeValue("'color", l); +}; + +/** + * Returns the x position of a component. + */ +scdl.x = function(l) { + return namedAttributeValue("'x", l); +}; + +/** + * Returns the y position of a component. + */ +scdl.y = function(l) { + return namedAttributeValue("'y", l); +}; + +/** + * Returns the implementation of a component. + */ +scdl.implementation = function(l) { + function filterImplementation(v) { + return isElement(v) && cadr(v).match("implementation.") != null; + } + + var n = filter(filterImplementation, l); + if (isNil(n)) + return null; + return car(n); +}; + +/** + * Returns the type of a component or componentType implementation. + */ +scdl.implementationType = function(l) { + return elementName(l).substring(1); +}; + +/** + * Returns the URI of a service, reference or implementation. + */ +scdl.uri = function(l) { + return namedAttributeValue("'uri", l); +}; + +/** + * Returns the align attribute of a service or reference. + */ +scdl.align = function(l) { + return namedAttributeValue("'align", l); +}; + +/** + * Returns the visible attribute of a service or reference. + */ +scdl.visible = function(l) { + return namedAttributeValue("'visible", l); +}; + +/** + * Returns the clonable attribute of a reference. + */ +scdl.clonable = function(l) { + return namedAttributeValue("'clonable", l); +}; + +/** + * Returns a list of services in a component or componentType. + */ +scdl.services = function(l) { + return namedElementChildren("'service", l); +}; + +/** + * Returns a list of references in a component or componentType. + */ +scdl.references = function(l) { + return namedElementChildren("'reference", l); +}; + +/** + * Returns a list of bindings in a service or reference. + */ +scdl.bindings = function(l) { + function filterBinding(v) { + return isElement(v) && cadr(v).match("binding.") != null; + } + + return filter(filterBinding, l); +}; + +/** + * Returns the type of a binding. + */ +scdl.bindingType = function(l) { + return elementName(l).substring(1); +}; + +/** + * Returns the target of a reference. + */ +scdl.target = function(l) { + function targetURI() { + function bindingsTarget(l) { + if (isNil(l)) + return null; + var u = scdl.uri(car(l)); + if (!isNil(u)) + return u; + return bindingsTarget(cdr(l)); + } + + var t = namedAttributeValue("'target", l); + if (!isNil(t)) + return t; + return bindingsTarget(scdl.bindings(l)); + } + var turi = targetURI(); + if (isNil(turi)) + return turi; + return car(tokens(turi)); +}; + +/** + * Returns a list of properties in a component or componentType. + */ +scdl.properties = function(l) { + return namedElementChildren("'property", l); +}; + +/** + * Returns the value of a property. + */ +scdl.propertyValue = function(l) { + if (!elementHasValue(l)) + return ''; + return elementValue(l); +}; + +/** + * Convert a list of elements to a name -> element assoc list. + */ +scdl.nameToElementAssoc = function(l) { + if (isNil(l)) + return l; + return cons(mklist(scdl.name(car(l)), car(l)), scdl.nameToElementAssoc(cdr(l))); +}; + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css new file mode 100644 index 0000000000..ddc21b2095 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css @@ -0,0 +1,709 @@ +/* + * 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. + */ + +body { +margin-top: 0px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px; +font-family: Arial; font-style: normal; font-variant: normal; font-size: 14px; +background-color: #ffffff; opacity: 1; +-webkit-text-size-adjust: none; +-webkit-touch-callout: none; +-webkit-tap-highlight-color: rgba(0,0,0,0); +-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; +cursor: default; +-webkit-backface-visibility: hidden; +} + +.delayed { +visibility: hidden; +} + +.fixed { +position: fixed; +} + +.devicewidth { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +} + +.mainbody { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible; +-webkit-backface-visibility: hidden; +} + +.viewcontainer3dm { +position: absolute; left: 0px; top: 35px; width: 100%; +-webkit-backface-visibility: hidden; +} + +.viewcontainer3d { +position: absolute; left: 0px; top: 35px; width: 100%; +-webkit-backface-visibility: hidden; +} + +.leftviewloading3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +z-index: -10; background-color: #ffffff; +-webkit-transform: translate(100%, 0px); +-moz-transform: translate(100%, 0px); +-ms-transform: translate(100%, 0px); +-o-transform: translate(100%, 0px); +transform: translate(100%, 0px); +-webkit-backface-visibility: hidden; +} + +.rightviewloading3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +z-index: -10; background-color: #ffffff; +-webkit-transform: translate(-100%, 0px); +-moz-transform: translate(-100%, 0px); +-ms-transform: translate(-100%, 0px); +-o-transform: translate(-100%, 0px); +transform: translate(-100%, 0px); +-webkit-backface-visibility: hidden; +} + +.viewloading3d { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +visibility: hidden; z-index: -10; background-color: #ffffff; +-webkit-transform: translate(100%, 0px); +-moz-transform: translate(100%, 0px); +-ms-transform: translate(100%, 0px); +-o-transform: translate(100%, 0px); +transform: translate(100%, 0px); +-webkit-backface-visibility: hidden; +} + +.viewloaded3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible; +z-index: 0; background-color: #ffffff; +-webkit-transition: -webkit-transform 0.4s ease-in-out; +-moz-transition: -moz-transform 0.4s ease-in-out; +-ms-transition: -ms-transform 0.4s ease-in-out; +-o-transition: -o-transform 0.4s ease-in-out; +transition: transform 0.4s ease-in-out; +-webkit-transform: translate(0px, 0px); +-moz-transform: translate(0px, 0px); +-ms-transform: translate(0px, 0px); +-o-transform: translate(0px, 0px); +transform: translate(0px, 0px); +-webkit-backface-visibility: hidden; +} + +.viewloaded3d { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible; +z-index: 0; background-color: #ffffff; +-webkit-transform: translate(0px, 0px); +-moz-transform: translate(0px, 0px); +-ms-transform: translate(0px, 0px); +-o-transform: translate(0px, 0px); +transform: translate(0px, 0px); +-webkit-backface-visibility: hidden; +} + +.viewunloading3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +z-index: 0; background-color: #ffffff; +-webkit-transform: translate(0px, 0px); +-moz-transform: translate(0px, 0px); +-ms-transform: translate(0px, 0px); +-o-transform: translate(0px, 0px); +transform: translate(0px, 0px); +-webkit-backface-visibility: hidden; +} + +.leftviewunloaded3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +z-index: -10; background-color: #ffffff; +-webkit-transition: -webkit-transform 0.4s ease-in-out; +-moz-transition: -moz-transform 0.4s ease-in-out; +-ms-transition: -ms-transform 0.4s ease-in-out; +-o-transition: -o-transform 0.4s ease-in-out; +transition: transform 0.4s ease-in-out; +-webkit-transform: translate(-100%, 0px); +-moz-transform: translate(-100%, 0px); +-ms-transform: translate(-100%, 0px); +-o-transform: translate(-100%, 0px); +transform: translate(-100%, 0px); +-webkit-backface-visibility: hidden; +} + +.rightviewunloaded3dm { +position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden; +z-index: -10; background-color: #ffffff; +-webkit-transition: -webkit-transform 0.4s ease-in-out; +-moz-transition: -moz-transform 0.4s ease-in-out; +-ms-transition: -ms-transform 0.4s ease-in-out; +-o-transition: -o-transform 0.4s ease-in-out; +transition: transform 0.4s ease-in-out; +-webkit-transform: translate(100%, 0px); +-moz-transform: translate(100%, 0px); +-ms-transform: translate(100%, 0px); +-o-transform: translate(100%, 0px); +transform: translate(100%, 0px); +-webkit-backface-visibility: hidden; +} + +.body { +width: 100%; height: 5000px; overflow: visible; +-webkit-backface-visibility: hidden; +} + +.viewhead { +position: absolute; left: 0px; top: 35px; height: 35px; line-height: 35px; width: 100%; z-index: 8; +font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; +overflow: hidden; +-webkit-backface-visibility: hidden; +} + +.viewheadbackground { +position: absolute; left: 0px; top: 35px; height: 35px; line-height: 35px; width: 2500px; z-index: 7; +background-color: #f1f1f1; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; +overflow: hidden; +-webkit-backface-visibility: hidden; +} + +.viewfoot { +position: absolute; left: 0px; bottom: 0px; height: 25px; line-height: 25px; width: 100%; z-index: 8; +font-size: 12px; background-color: #ffffff; color: #404040; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff; +overflow: hidden; +-webkit-backface-visibility: hidden; +} + +.status { +position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9; +font-size: 12px; background-color: #ffffff; color: #404040; +border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff; +overflow: hidden; +-webkit-transform: translate(0px, 0px); +-moz-transform: translate(0px, 0px); +-ms-transform: translate(0px, 0px); +-o-transform: translate(0px, 0px); +transform: translate(0px, 0px); +-webkit-backface-visibility: hidden; +} + +.statusout3 { +position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9; +font-size: 12px; background-color: #ffffff; color: #404040; +border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff; +overflow: hidden; +-webkit-transition: -webkit-transform 0.4s ease-in-out 3s; +-moz-transition: -moz-transform 0.4s ease-in-out 3s; +-ms-transition: -ms-transform 0.4s ease-in-out 3s; +-o-transition: -o-transform 0.4s ease-in-out 3s; +transition: transform 0.4s ease-in-out 3s; +-webkit-transform: translate(0px, 35px); +-moz-transform: translate(0px, 35px); +-ms-transform: translate(0px, 35px); +-o-transform: translate(0px, 35px); +transform: translate(0px, 35px); +-webkit-backface-visibility: hidden; +} + +.statusout1 { +position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9; +font-size: 12px; background-color: #ffffff; color: #404040; +border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff; +overflow: hidden; +-webkit-transition: -webkit-transform 0.4s ease-in-out 1s; +-moz-transition: -moz-transform 0.4s ease-in-out 1s; +-ms-transition: -ms-transform 0.4s ease-in-out 1s; +-o-transition: -o-transform 0.4s ease-in-out 1s; +transition: transform 0.4s ease-in-out 1s; +-webkit-transform: translate(0px, 35px); +-moz-transform: translate(0px, 35px); +-ms-transform: translate(0px, 35px); +-o-transform: translate(0px, 35px); +transform: translate(0px, 35px); +-webkit-backface-visibility: hidden; +} + +.okstatus { +font-size: 110%; font-weight: bold; padding-left: 6px; padding-right: 6px; white-space: nowrap; text-decoration: none; +background-color: #f1f1f1; color: #000000; +width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right; +} + +.errorstatus { +font-size: 110%; font-weight: bold; padding-left: 6px; padding-right: 6px; white-space: nowrap; text-decoration: none; +background-color: #d14836; color: #ffffff; +width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right; +} + +.viewfootbackground { +position: absolute; left: 0px; bottom: 0px; height: 25px; line-height: 25px; width: 2500px; z-index: 7; +background-color: #ffffff; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff; +overflow: hidden; +-webkit-backface-visibility: hidden; +} + +.viewcontent { +position: absolute; left: 0px; top: 38px; width: 100%; +-webkit-backface-visibility: hidden; +} + +.viewform { +position: absolute; left: 0px; top: 40px; width: 100%; +} + +table { +border: 0px; border-collapse: collapse; border-color: #a2bae7; border-style: solid; +font-family: Arial; font-style: normal; font-variant: normal; font-size: 14px; +overflow: visible; +} + +.trb { +border-bottom: 1px; border-bottom-style: solid; border-color: #dcdcdc; +} + +th { +font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 0px; padding-bottom: 0px; vertical-align: middle; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; border-left-color: #e5e5e5; border-right-color: #e5e5e5; +overflow: hidden; +} + +.section { +font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000; height: 30px; line-height: 30px; +text-align: left; padding-top: 0px; padding-bottom: 0px; padding-left: 2px; padding-right: 2px; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; border-left-color: #e5e5e5; border-right-color: #e5e5e5; +overflow: hidden; +} + +.hsection { +width: 100%; height: 0px; visibility: hidden; +border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-style: solid; border-bottom-color: #000000; background-color: #ffffff; +padding-left: 2px; padding-right: 2px; padding-top: 0px; padding-bottom: 0px; margin-bottom: 0px; margin-left: auto; margin-right: auto; text-align: center; +} + +.fsection{ +width: 100%; height: 0px; visibility: hidden; +border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #a2bae7; +padding: 0px; margin-top: 0px; margin-left: auto; margin-right: auto; text-align: center; +} + +.bluetext { +color: #4787ed; +} + +.redtext { +color: #d14836; +} + +.mirror { +display: inline-block; +-webkit-transform: scaleX(-1); +-moz-transform: scaleX(-1); +-ms-transform: scaleX(-1); +-o-transform: scaleX(-1); +transform: scaleX(-1); +} + +.greentext { +color: #009900; +} + +.text { +padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap; +} + +.link { +padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap; +} + +.checkbox { +padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap; +} + +.thl { +border-left: 0px; +} + +.thr { +border-right: 0px; +} + +.ths { +padding: 0px; +} + +td { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: nowrap; vertical-align: middle; border: 0px; +} + +.tdl { +border-right: 1px; border-style: solid; border-color: #a2bae7; width: 10px; +} + +.tdr { +border-left: 1px; border-style: solid; border-color: #a2bae7; +} + +.tdw { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: normal; vertical-align: middle; +} + +.datatd { +border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: middle; +} + +.datatdl { +border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: middle; +} + +.datatdltop { +border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: top; +} + +.datatdr { +border-left: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; vertical-align: middle; +} + +.datatable { +border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; +overflow: visible; +} + +.databg { +opacity: .6; +-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; +filter: alpha(opacity=60); +} + +.guide { +border: 1px; border-style: solid; border-color: #c0c0c0; +} + +iframe { +border: 0px; margin: 0px; padding: 0px; +} + +.fakeframe { +padding: 3px; background-color: #dcdcdc; color: #000000; +} + +input { +vertical-align: middle; +font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px; +outline: none; +-webkit-text-size-adjust: 100%; +} + +button { +vertical-align: middle; +font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px; +outline: none; +-webkit-text-size-adjust: 100%; +} + +textarea { +font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px; +outline: none; +overflow: auto; resize: none; +} + +.flatentry { +font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px; +-webkit-appearance: none; appearance: none; +border: 1px solid #d9d9d9!important; border-top: 1px solid silver!important; +box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; +border-radius: 1px; -webkit-border-radius: 1px; -moz-border-radius: 1px; +margin: 1px!important; padding: 3px 1px 3px 3px; +} + +.graphentry { +font-family: Arial; font-style: normal; font-variant: normal; font-size: 13px; +-webkit-appearance: none; appearance: none; +border: 1px solid #d9d9d9!important; border-top: 1px solid silver!important; +box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; +border-radius: 1px; -webkit-border-radius: 1px; -moz-border-radius: 1px; +margin: 0px; padding: 0px; +} + +.flatcheckbox { +} + +.editablewidget { +-webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; +outline: none; -moz-outline-style: none; +} + +.noneditablewidget { +outline: none; -moz-outline-style: none; +} + +.editablesvg { +background-color: transparent; +font-family: inherit; font-style: inherit; font-variant: inherit; font-size: inherit; font-weight: inherit; +padding: 0px; margin: 0px; +overflow: auto; resize: none; +outline: none; -moz-outline-style: none; +-webkit-appearance: none; -webkit-text-size-adjust: 100%; +border: 0px; +} + +a:link { +color: #357ae8; text-decoration: none; white-space: nowrap; cursor: pointer; +} + +a:visited { +color: #357ae8; text-decoration: none; white-space: nowrap; cursor: pointer; +} + +.tbarmenu { +position: absolute; top: 0px; left: 0px; z-index: 10; width: 100%; margin: 0px; padding: 0px; border-collapse: separate; +height: 35px; line-height: 35px; background-color: #2c2c2c; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #2c2c2c; border-bottom-color: #2c2c2c; +-webkit-backface-visibility: hidden; +} + +.tbarbackground { +position: absolute; top: 0px; left: 0px; z-index: 9; width: 2500px; margin: 0px; padding: 0px; border-collapse: separate; +height: 35px; line-height: 35px; background-color: #2c2c2c; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #2c2c2c; border-bottom-color: #2c2c2c; +overflow: hidden; +-webkit-backface-visibility: hidden; +} + +.tbarleft { +padding-left: 2px; padding-right: 6px; white-space: nowrap; float: left; +} + +.tbarright { +padding-left: 6px; padding-right: 2px; white-space: nowrap; float: right; +} + +.tbaramenu { +font-size: 110%; color: #cccccc; text-decoration: none; white-space: nowrap; +} + +.tbarsmenu { +font-size: 110%; font-weight: bold; color: #ffffff; text-decoration: none; white-space: nowrap; +} + +.amenu { +padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #808080; text-decoration: none; float: left; +} + +.smenu { +padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none; float: left; +} + +.cmenu { +font-size: 18px; padding-left: 6px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none; +width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right; +} + +.bcmenu { +font-size: 22px; padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none; +} + +.rmenu { +padding-left: 2px; padding-right: 2px; white-space: nowrap; white-space: nowrap; float: right; +} + +h1 { +font-size: 150%; font-weight: bold; vertical-align: middle; margin-top: 5px; margin-bottom: 5px; margin-left: 2px; margin-right: 2px; white-space: nowrap; +} + +h2 { +font-size: 120%; font-weight: bold; vertical-align: middle; margin-top: 5px; margin-bottom: 5px; margin-left: 2px; margin-right: 2px; white-space: nowrap; +} + +.hd1 { +font-size: 150%; font-weight: bold; white-space: nowrap; +} + +.hd2 { +font-size: 120%; font-weight: bold; white-space: nowrap; +} + +img { +border: 0px; +} + +.plusminus { +font-size: 18px; font-family: "Courier New"; +} + +.imgbutton { +width: 142px; height: 64px; margin-left: 20px; margin-right: 20px; padding: 0px; border: 1px; cursor: pointer; +} + +.graybutton { +display: inline-block; text-align: center; color: #444; font-weight: bold; +padding-top: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 4px; +height: 28px; line-height: 28px; min-width: 30px; +-webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; +border: 1px solid gainsboro; background-color: whiteSmoke; +background-image: -webkit-gradient(linear,left top,left bottom,from(whiteSmoke),to(#f1f1f1)); +background-image: -webkit-linear-gradient(top,whiteSmoke,#f1f1f1); +background-image: -moz-linear-gradient(top,whiteSmoke,#f1f1f1); +background-image: -ms-linear-gradient(top,whiteSmoke,#f1f1f1); +background-image: -o-linear-gradient(top,whiteSmoke,#f1f1f1); +background-image: linear-gradient(top,whiteSmoke,#f1f1f1); +cursor: default; +} + +.graybutton:hover { +border: 1px solid #c6c6c6; color: #333; text-shadow: 0 1px rgba(0, 0, 0, 0.3); background-color: #f8f8f8; +background-image: -webkit-gradient(linear,left top,left bottom,from(#f8f8f8),to(#f1f1f1)); +background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1); +background-image: -moz-linear-gradient(top,#f8f8f8,#f1f1f1); +background-image: -ms-linear-gradient(top,#f8f8f8,#f1f1f1); +background-image: -o-linear-gradient(top,#f8f8f8,#f1f1f1); +background-image: linear-gradient(top,#f8f8f8,#f1f1f1); +} + +.graybutton:active { +background-color: #f6f6f6; +background-image: -webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#f1f1f1)); +background-image: -webkit-linear-gradient(top,#f6f6f6,#f1f1f1); +background-image: -moz-linear-gradient(top,#f6f6f6,#f1f1f1); +background-image: -ms-linear-gradient(top,#f6f6f6,#f1f1f1); +background-image: -o-linear-gradient(top,#f6f6f6,#f1f1f1); +background-image: linear-gradient(top,#f6f6f6,#f1f1f1); +-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.graybutton:disabled { +color: #c0c0c0; +} + +.bluebutton { +border: 1px solid #3079ed; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.1); background-color: #4d90fe; +background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed)); +background-image: -webkit-linear-gradient(top,#4d90fe,#4787ed); +background-image: -moz-linear-gradient(top,#4d90fe,#4787ed); +background-image: -ms-linear-gradient(top,#4d90fe,#4787ed); +background-image: -o-linear-gradient(top,#4d90fe,#4787ed); +background-image: linear-gradient(top,#4d90fe,#4787ed); +} + +.bluebutton:disabled { +color: #c0c0c0; +} + +.bluebutton:hover { +border: 1px solid #2f5bb7; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.3); background-color: #357ae8; +background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8)); +background-image: -webkit-linear-gradient(top,#4d90fe,#357ae8); +background-image: -moz-linear-gradient(top,#4d90fe,#357ae8); +background-image: -ms-linear-gradient(top,#4d90fe,#357ae8); +background-image: -o-linear-gradient(top,#4d90fe,#357ae8); +background-image: linear-gradient(top,#4d90fe,#357ae8); +} + +.bluebutton:hover:disabled { +color: #c0c0c0; +} + +.redbutton { +border: 1px solid transparent; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.1); background-color: #d14836; +background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836)); +background-image: -webkit-linear-gradient(top,#dd4b39,#d14836); +background-image: -moz-linear-gradient(top,#dd4b39,#d14836); +background-image: -ms-linear-gradient(top,#dd4b39,#d14836); +background-image: -o-linear-gradient(top,#dd4b39,#d14836); +background-image: linear-gradient(top,#dd4b39,#d14836); +-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); -ms-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); +-o-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); +} + +.redbutton:disabled { +color: #c0c0c0; +} + +.redbutton:hover { +border: 1px solid #b0281a; border-bottom-color: #af301f; color: white; background-color: #c53727; +background-image: -webkit-linear-gradient(top,#dd4b39,#c53727); +background-image: -moz-linear-gradient(top,#dd4b39,#c53727); +background-image: -ms-linear-gradient(top,#dd4b39,#c53727); +background-image: -o-linear-gradient(top,#dd4b39,#c53727); +background-image: linear-gradient(top,#dd4b39,#c53727); +-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -ms-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); +-o-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); +} + +.redbutton:hover:disabled { +color: #c0c0c0; +} + +.box { +width: 150px; display: inline-block; +border: 1px; border-style: solid; border-color: #dcdcdc; border-collapse: collapse; +white-space: nowrap; margin: 2px; padding: 2px; vertical-align: top; +} + +.appicon { +float: left; +height: 50px; width: 50px; vertical-align: top; margin: 0px; padding: 0px; +} + +.apptitle { +font-weight: bold; +} + +.note { +font-size: 12px; color: #808080; +} + +.pagediv { +position: absolute; display: block; overflow: visible; +-webkit-backface-visibility: hidden; +} + +.graphdiv { +position: absolute; display: block; overflow: visible; +-webkit-backface-visibility: hidden; +} + +.g { +position: absolute; display: block; overflow: visible; +-webkit-backface-visibility: hidden; +} + +.path { +position: absolute; background: transparent; display: block; overflow: visible; +visibility: visible; +-webkit-backface-visibility: hidden; +} + +.gtitle { +position: absolute; +margin: 4px; padding: 0px; line-height: 15px; vertical-align: middle; white-space: nowrap; +font-family: Arial; font-style: normal; font-variant: normal; font-size: 13px; cursor: default; +background: transparent; display: block; overflow: visible; +-webkit-text-size-adjust: none; +-webkit-touch-callout: none; +-webkit-tap-highlight-color: rgba(0,0,0,0); +-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js new file mode 100644 index 0000000000..fb53598390 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js @@ -0,0 +1,409 @@ +/* + * 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. + */ + +/** + * UI utility functions. + */ + +var ui = {}; + +/** + * Return a child element of a node with the given id. + */ +ui.elementByID = function(node, id) { + if (node.skipNode == true) + return null; + for (var i in node.childNodes) { + var child = node.childNodes[i]; + if (child.id == id) + return child; + var gchild = ui.elementByID(child, id); + if (gchild != null) + return gchild; + } + return null; +}; + +/** + * Return the current document, or a child element with the given id. + */ +function $(id) { + if (id == document) + return document; + return ui.elementByID($(document), id); +}; + +/** + * Return a list of elements with the given class name. + */ +ui.elementsByClassName = function(node, c) { + return nodeList(node.getElementsByClassName(c)); +}; + +/** + * Return the query string of a URL. + */ +ui.query = function(url) { + var u = '' + url; + var q = u.indexOf('?'); + return q >= 0? u.substring(q + 1) : ''; +}; + +/** + * Return the fragment part of a URL. + */ +ui.fragment = function(url) { + var u = '' + url; + var h = u.indexOf('#'); + return h >= 0? u.substring(h + 1) : ''; +}; + +/** + * Return the path and parameters of a URL. + */ +ui.pathandparams = function(url) { + var u = '' + url; + var ds = u.indexOf('//'); + var u2 = ds > 0? u.substring(ds + 2) : u; + var s = u2.indexOf('/'); + return s > 0? u2.substring(s) : ''; +}; + +/** + * Return a dictionary of query parameters in a URL. + */ +ui.queryParams = function(url) { + var qp = new Array(); + var qs = ui.query(url).split('&'); + for (var i = 0; i < qs.length; i++) { + var e = qs[i].indexOf('='); + if (e > 0) + qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); + } + return qp; +}; + +/** + * Return a dictionary of fragment parameters in a URL. + */ +ui.fragmentParams = function(url) { + var qp = new Array(); + var qs = ui.fragment(url).split('&'); + for (var i = 0; i < qs.length; i++) { + var e = qs[i].indexOf('='); + if (e > 0) + qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); + } + return qp; +}; + +/** + * Convert a base64-encoded image to a data URL. + */ +ui.b64img = function(b64) { + return 'data:image/png;base64,' + b64; +}; + +/** + * Declare a CSS stylesheet. + */ +ui.declareCSS = function(s) { + var e = document.createElement('style'); + e.type = 'text/css'; + e.textContent = s; + return e; +}; + +/** + * Declare a script. + */ +ui.declareScript = function(s) { + var e = document.createElement('script'); + e.type = 'text/javascript'; + e.text = s; + return e; +}; + +/** + * Return the scripts elements under a given element. + */ +ui.innerScripts = function(e) { + return map(function(s) { return s.text; }, nodeList(e.getElementsByTagName('script'))); +}; + +/** + * Evaluate a script. + */ +ui.evalScript = function(s) { + return eval('(function() {\n' + s + '\n})();'); +}; + +/** + * Include a script. + */ +ui.includeScript = function(s) { + //log('include', s); + return eval(s); +}; + +/** + * Return true if the client is a mobile device. + */ +ui.mobiledetected = false; +ui.mobile = false; +ui.isMobile = function() { + if (ui.mobiledetected) + return ui.mobile; + var ua = navigator.userAgent; + if (ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/Android/i) || ua.match(/Blackberry/i) || ua.match(/WebOs/i)) + ui.mobile = true; + ui.mobiledetected = true; + return ui.mobile; +}; + +/** + * Convert a CSS position to a numeric position. + */ +ui.numpos = function(p) { + return p == ''? 0 : Number(p.substr(0, p.length - 2)); +}; + +/** + * Convert a numeric position to a CSS pixel position. + */ +ui.pixpos = function(p) { + return p + 'px'; +}; + +/** + * Default orientation change behavior. + */ +ui.onorientationchange = function(e) { + + // Scroll to the top and hide the address bar + window.scrollTo(0, 0); + + // Change fixed position elements to absolute then back to fixed + // to make sure they're correctly layed out after the orientation + // change + map(function(e) { + e.style.position = 'absolute'; + return e; + }, ui.elementsByClassName(document, 'fixed')); + + setTimeout(function() { + map(function(e) { + e.style.position = 'fixed'; + return e; + }, ui.elementsByClassName(document, 'fixed')); + }, 0); + return true; +}; + +/** + * Default page load behavior. + */ +ui.onload = function() { + + // Scroll to the top and hide the address bar + window.scrollTo(0, 0); + + // Initialize fixed position elements only after the page is loaded, + // to workaround layout issues with fixed position on mobile devices + setTimeout(function() { + map(function(e) { + e.style.position = 'fixed'; + return e; + }, ui.elementsByClassName(document, 'fixed')); + }, 0); + return true; +}; + +/** + * Navigate to a new document. + */ +ui.navigate = function(url, win) { + //log('navigate', url, win); + + // Open a new window + if (win == '_blank') { + window.top.open(url, win); + return false; + } + + // Open a new document in the current window + if (win == '_self') { + window.top.open(url, win); + return false; + } + + // Reload the current window + if (win == '_reload') { + window.top.location = url; + window.top.location.reload(); + return false; + } + + // Let the current top window handle the navigation + if (win == '_view') { + if (!window.top.onnavigate) + return window.top.open(url, '_self'); + window.top.onnavigate(url); + return false; + } + + window.top.open(url, win); + return false; +} + +/** + * Build a portable <a href> tag. + */ +ui.href = function(id, loc, target, html) { + if (target == '_blank') + return '<a id="' + id + '" href="' + loc + '" target="_blank">' + html + '</a>'; + return '<a id="' + id + '" href="' + loc + '" onclick="return ui.navigate(\'' + loc + '\', \'' + target + '\');">' + html + '</a>'; +}; + +/** + * Build a menu bar. + */ +ui.menu = function(id, name, href, target, hilight) { + function Menu() { + this.content = function() { + if (hilight == true) + return ui.href(id, href, target, '<span class="tbarsmenu">' + name + '</span>'); + else if (hilight == false) + return ui.href(id, href, target, '<span class="tbaramenu">' + name + '</span>'); + else + return ui.href(id, href, target, '<span class="' + hilight + '">' + name + '</span>'); + }; + } + return new Menu(); +}; + +ui.menufunc = function(id, name, fun, hilight) { + function Menu() { + this.content = function() { + function href(id, fun, html) { + return '<a id="' + id + '" href="/" onclick="' + fun + '">' + html + '</a>'; + } + + if (hilight == true) + return href(id, fun, '<span class="tbarsmenu">' + name + '</span>'); + else if (hilight == false) + return href(id, fun, '<span class="tbaramenu">' + name + '</span>'); + else + return href(id, fun, '<span class="' + hilight + '">' + name + '</span>'); + }; + } + return new Menu(); +}; + +ui.menubar = function(left, right) { + var bar = ''; + for (i in left) + bar = bar + '<span class="tbarleft">' + left[i].content() + '</span>'; + for (i in right) + bar = bar + '<span class="tbarright">' + right[i].content() + '</span>'; + return bar; +}; + +/** + * Convert a list of elements to an HTML table. + */ +ui.datatable = function(l) { + + function indent(i) { + if (i == 0) + return ''; + return ' ' + indent(i - 1); + } + + function rows(l, i) { + if (isNil(l)) + return ''; + var e = car(l); + + // Convert a list of simple values into a list of name value pairs + if (!isList(e)) + return rows(expandElementValues("'value", l), i); + + // Convert a list of complex values into a list of name value pairs + if (isList(car(e))) + return rows(expandElementValues("'value", l), i); + + // Generate table row for a simple element value + if (elementHasValue(e)) { + var v = elementValue(e); + if (!isList(v)) { + return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' + + '<td class="datatdr tdw">' + (v != null? v : '') + '</td></tr>' + + rows(cdr(l), i); + } + + return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i); + } + + // Generate table row for an element with children + return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' + + '<td class="datatdr tdw">' + '</td></tr>' + + rows(elementChildren(e), i + 1) + + rows(cdr(l), i); + } + + return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>'; +} + +/** + * Convert a list of elements to an HTML single column table. + */ +ui.datalist = function(l) { + + function rows(l, i) { + if (isNil(l)) + return ''; + var e = car(l); + + // Convert a list of simple values into a list of name value pairs + if (!isList(e)) + return rows(expandElementValues("'value", l), i); + + // Convert a list of complex values into a list of name value pairs + if (isList(car(e))) + return rows(expandElementValues("'value", l), i); + + // Generate table row for a simple element value + if (elementHasValue(e)) { + var v = elementValue(e); + if (!isList(v)) { + return '<tr><td class="datatd tdw">' + (v != null? v : '') + '</td></tr>' + + rows(cdr(l), i); + } + + return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i); + } + + // Generate rows for an element's children + return rows(elementChildren(e), i + 1) + rows(cdr(l), i); + } + + return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>'; +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js new file mode 100644 index 0000000000..0f7de94289 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js @@ -0,0 +1,471 @@ +/* + * 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. + */ +function cons(car, cdr) { + var a = new Array(); + a.push(car); + return a.concat(cdr); +} + +function car(l) { + return l[0]; +} + +function first(l) { + return l[0]; +} + +function cdr(l) { + return l.slice(1); +} + +function rest(l) { + return l.slice(1); +} + +function cadr(l) { + return l[1]; +} + +function cddr(l) { + return l.slice(2); +} + +function caddr(l) { + return l[2]; +} + +function cdddr(l) { + return l.slice(3); +} + +function cadddr(l) { + return l[3]; +} + +function append(a, b) { + return a.concat(b); +} + +function reverse(l) { + return l.slice(0).reverse(); +} + +function range(a, b) { + var l = new Array(); + for (var x = a; x < b; x++) + l.push(x); + return l; +} + +function isNil(v) { + return (v == null || typeof v == 'undefined' || (v.constructor == Array && v.length == 0)); +} + +function isSymbol(v) { + return (typeof v == 'string' && v.slice(0, 1) == "'"); +} + +function isString(v) { + return (typeof v == 'string' && v.slice(0, 1) != "'"); +} + +function isList(v) { + return (v != null && typeof v != 'undefined' && v.constructor == Array); +} + +function isTaggedList(v, t) { + return (isList(v) && !isNil(v) && car(v) == t); +} + +var emptylist = new Array(); + +function mklist() { + if (arguments.length == 0) + return emptylist; + var a = new Array(); + for (i = 0; i < arguments.length; i++) + a[i] = arguments[i]; + return a; +} + +function length(l) { + return l.length; +} + +/** + * Scheme-like associations. + */ +function assoc(k, l) { + if (isNil(l)) + return emptylist; + var n = l.length; + for(var i = 0; i < n; i++) { + if (k == car(l[i])) + return l[i]; + } + return emptylist; +} + +/** + * Map, filter and reduce functions. + */ +function map(f, l) { + if (isNil(l)) + return l; + var n = l.length; + var a = new Array(); + for(var i = 0; i < n; i++) { + a.push(f(l[i])); + } + return a; +} + +function filter(f, l) { + if (isNil(l)) + return l; + var n = l.length; + var a = new Array(); + for(var i = 0; i < n; i++) { + if (f(l[i])) + a.push(l[i]); + } + return a; +} + +function reduce(f, i, l) { + if (isNil(l)) + return i; + return reduce(f, f(i, car(l)), cdr(l)); +} + +/** + * Split a path into a list of segments. + */ +function tokens(path) { + return filter(function(s) { return length(s) != 0; }, path.split("/")); +} + +/** + * Debug log a value. + */ +var rconsole; + +function debug(v) { + try { + var s = ''; + for (i = 0; i < arguments.length; i++) { + s = s + writeValue(arguments[i]); + if (i < arguments.length) + s = s + ' '; + } + + if (rconsole) { + try { + rconsole.log(s); + } catch (e) {} + } + try { + console.log(s); + } catch (e) {} + } catch (e) {} + return true; +} + +/** + * Dump an object to the console. + */ +function dump(o) { + try { + for (f in o) { + try { + debug('dump ' + f + '=' + o[f]); + } catch (e) {} + } + } catch (e) {} + return true; +} + +/** + * Return true if the current browser is Internet Explorer. + */ +function isIE() { + if (typeof isIE.detected != 'undefined') + return isIE.detected; + isIE.detected = navigator.appName == 'Microsoft Internet Explorer'; + return isIE.detected; +}; + +/** + * External build configuration. + */ +var config; +if (isNil(config)) + config = {}; + +/** + * Simple assert function. + */ +function AssertException() { +} + +AssertException.prototype.toString = function () { + return 'AssertException'; +}; + +function assert(exp) { + if (!exp) + throw new AssertException(); +} + +/** + * Write a list of strings. + */ +function writeStrings(l) { + if (isNil(l)) + return ''; + var s = ''; + var n = l.length; + for(var i = 0; i < n; i++) { + s = s + l[i]; + } + return s; +} + +/** + * Write a value using a Scheme-like syntax. + */ +function writeValue(v) { + function writePrimitive(p) { + if (isSymbol(p)) + return '' + p.substring(1); + if (isString(p)) + return '"' + p + '"'; + return '' + p; + } + + function writeList(l) { + if (isNil(l)) + return ''; + return ' ' + writeValue(car(l)) + writeList(cdr(l)); + } + + if (!isList(v)) + return writePrimitive(v); + if (isNil(v)) + return '()'; + return '(' + writeValue(car(v)) + writeList(cdr(v)) + ')'; +} + +/** + * Apply a function and memoize its result. + */ +function memo(obj, key, f) { + if (!('memo' in obj)) { + obj.memo = {}; + return obj.memo[key] = f(); + } + if (key in obj.memo) + return obj.memo[key]; + return obj.memo[key] = f(); +} + +/** + * Un-memoize stored results. + */ +function unmemo(obj) { + obj.memo = {}; + return true; +} + +/** + * Local storage. + */ +var lstorage = {}; +lstorage.enabled = true; + +/** + * Get an item. + */ +lstorage.getItem = function(k) { + if (!lstorage.enabled) + return null; + try { + return localStorage.getItem(k); + } catch(e) { + return null; + } +} + +/** + * Set an item. + */ +lstorage.setItem = function(k, v) { + if (!lstorage.enabled) + return null; + try { + return localStorage.setItem(k, v); + } catch(e) { + return null; + } +} + +/** + * Remove an item. + */ +lstorage.removeItem = function(k) { + if (!lstorage.enabled) + return null; + try { + return localStorage.removeItem(k); + } catch(e) { + return null; + } +} + +/** + * Returns a list of the properties of an object. + */ +function properties(o) { + var a = new Array(); + for (p in o) + a.push(p); + return a; +} + +/** + * Convert a host name to a domain name. + */ +function domainname(host) { + var ds = host.indexOf('//'); + if (ds != -1) + return domainname(host.substring(ds + 2)); + var s = host.indexOf('/'); + if (s != -1) + return domainname(host.substring(0, s)); + var h = reverse(host.split('.')); + var d = (!isNil(cddr(h)) && caddr(h) == 'www')? mklist(car(h), cadr(h), caddr(h)) : mklist(car(h), cadr(h)); + return reverse(d).join('.'); +} + +/** + * Return true if a host name is a subdomain. + */ +function issubdomain(host) { + return host.split('.').length > 2; +} + +/** + * Return true if the document cookie contains auth information. + */ +function hasauthcookie() { + return !isNil(document.cookie) && + (document.cookie.indexOf('TuscanyOpenAuth=') != -1 || + document.cookie.indexOf('TuscanyOAuth1=') != -1 || + document.cookie.indexOf('TuscanyOAuth2=') != -1 || + document.cookie.indexOf('TuscanyOpenIDAuth=') != -1); +} + +/** + * Clear auth information from the document cookie. + */ +function clearauthcookie() { + document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; + document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; + document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; + document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; + return true; +} + +/** + * Format a string like Python format. + */ +function format() { + var i = 0; + var s = ''; + for (a in arguments) { + s = i == 0? arguments[a] : s.replace('{' + a + '}', arguments[a]); + i++; + } + return s; +} + +/** + * Functions with side effects. Use with moderation. + */ + +/** + * Set the car of a list. + */ +function setcar(l, v) { + l[0] = v; + return l; +} + +/** + * Set the cadr of a list. + */ +function setcadr(l, v) { + l[1] = v; + return l; +} + +/** + * Set the caddr of a list. + */ +function setcaddr(l, v) { + l[2] = v; + return l; +} + +/** + * Append the elements of a list to a list. + */ +function setappend(a, b) { + if (isNil(b)) + return a; + a.push(car(b)); + return setappend(a, cdr(b)); +} + +/** + * Set the cdr of a list. + */ +function setcdr(a, b) { + a.length = 1; + return setappend(a, b); +} + +/** + * Set the contents of a list. + */ +function setlist(a, b) { + if (b == a) + return b; + a.length = 0; + return setappend(a, b); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js new file mode 100644 index 0000000000..4d943cce75 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js @@ -0,0 +1,256 @@ +/* + * 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. + */ + +/** + * Convert a DOM node list to a regular list. + */ +function nodeList(n) { + var l = new Array(); + if (isNil(n)) + return l; + for (var i = 0; i < n.length; i++) + l[i] = n[i]; + return l; +} + +/** + * Append a list of nodes to a parent node. + */ +function appendNodes(nodes, p) { + if (isNil(nodes)) + return p; + p.appendChild(car(nodes)); + return appendNodes(cdr(nodes), p); +}; + +/** + * Return the child attributes of an element. + */ +function childAttributes(e) { + return filter(function(n) { return n.nodeType == 2; }, nodeList(e.attributes)); +} + +/** + * Return the child elements of an element. + */ +function childElements(e) { + return filter(function(n) { return n.nodeType == 1; }, nodeList(e.childNodes)); +} + +/** + * Return the child text nodes of an element. + */ +function childText(e) { + function trim(s) { + return s.replace(/^\s*/, '').replace(/\s*$/, ''); + } + return filter(function(n) { return n.nodeType == 3 && trim(n.nodeValue) != ''; }, nodeList(e.childNodes)); +} + +/** + * Read a list of XML attributes. + */ +function readAttributes(p, a) { + if (isNil(a)) + return a; + var x = car(a); + return cons(mklist(attribute, "'" + x.nodeName, x.nodeValue), readAttributes(p, cdr(a))); +} + +/** + * Read an XML element. + */ +function readElement(e, childf) { + var l = append(append(mklist(element, "'" + e.nodeName), readAttributes(e, childf(e))), readElements(childElements(e), childf)); + var t = childText(e); + if (isNil(t)) + return l; + return append(l, mklist(car(t).nodeValue)); +} + +/** + * Read a list of XML elements. + */ +function readElements(l, childf) { + if (isNil(l)) + return l; + return cons(readElement(car(l), childf), readElements(cdr(l), childf)); +} + +/** + * Return true if a list of strings contains an XML document. + */ +function isXML(l) { + if (isNil(l)) + return false; + return car(l).substring(0, 5) == '<?xml'; +} + +/** + * Parse a list of strings representing an XML document. + */ +function parseXML(l) { + var s = writeStrings(l); + var p = new DOMParser(); + return p.parseFromString(s, "text/xml"); +} + +/** + * Read a list of values from an XML document. + */ +function readXMLDocument(doc) { + var root = childElements(doc); + if (isNil(root)) + return mklist(); + return mklist(readElement(car(root), childAttributes)); +} + +/** + * Read a list of values from an XHTML element. + */ +function readXHTMLElement(xhtml) { + // Special XHTML attribute filtering on IE + function ieChildAttributes(e) { + var a = filter(function(n) { + // Filter out empty and internal DOM attributes + if (n.nodeType != 2 || isNil(n.nodeValue) || n.nodeValue == '') + return false; + if (n.nodeName == 'contentEditable' || n.nodeName == 'maxLength' || n.nodeName == 'loop' || n.nodeName == 'start') + return false; + return true; + }, nodeList(e.attributes)); + + if (e.style.cssText == '') + return a; + + // Add style attribute + var sa = new Object(); + sa.nodeName = 'style'; + sa.nodeValue = e.style.cssText; + return cons(sa, a); + } + + var childf = (typeof(XMLSerializer) != 'undefined')? childAttributes : ieChildAttributes; + return mklist(readElement(xhtml, childf)); +} + +/** + * Read a list of values from a list of strings representing an XML document. + */ +function readXML(l) { + return readXMLDocument(parseXML(l)); +} + +/** + * Return a list of strings representing an XML document. + */ +function writeXMLDocument(doc) { + if (typeof(XMLSerializer) != 'undefined') + return mklist(new XMLSerializer().serializeToString(doc)); + return mklist(doc.xml); +} + +/** + * Write a list of XML element and attribute tokens. + */ +function expandElementValues(n, l) { + if (isNil(l)) + return l; + return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l))); +} + +function writeList(l, node, doc) { + if (isNil(l)) + return node; + + var token = car(l); + if (isTaggedList(token, attribute)) { + if (isIE()) { + var aname = attributeName(token).substring(1); + if (aname != 'xmlns') + node.setAttribute(aname, '' + attributeValue(token)); + } else + node.setAttribute(attributeName(token).substring(1), '' + attributeValue(token)); + + } else if (isTaggedList(token, element)) { + + function mkelem(tok, doc) { + function xmlns(l) { + if (isNil(l)) + return null; + var t = car(l); + if (isTaggedList(t, attribute)) { + if (attributeName(t).substring(1) == 'xmlns') + return attributeValue(t); + } + return xmlns(cdr(l)); + } + + var ns = xmlns(elementChildren(tok)); + if (isIE()) + return doc.createElementNS(ns != null? ns : node.namespaceURI, elementName(tok).substring(1)); + if (ns == null) + return doc.createElement(elementName(tok).substring(1)); + return doc.createElementNS(ns, elementName(tok).substring(1)); + } + + if (elementHasValue(token)) { + var v = elementValue(token); + if (isList(v)) { + var e = expandElementValues(elementName(token), v); + writeList(e, node, doc); + } else { + var child = mkelem(token, doc); + writeList(elementChildren(token), child, doc); + node.appendChild(child); + } + } else { + var child = mkelem(token, doc); + writeList(elementChildren(token), child, doc); + node.appendChild(child); + } + } else + node.appendChild(doc.createTextNode('' + token)); + + writeList(cdr(l), node, doc); + return node; +} + +/** + * Make a new XML document. + */ +function mkXMLDocument() { + return document.implementation.createDocument('', '', null); +} + +/** + * Convert a list of values to a list of strings representing an XML document. + */ +function writeXML(l, xmlTag) { + var doc = mkXMLDocument(); + writeList(l, doc, doc); + if (!xmlTag) + return writeXMLDocument(doc); + return mklist('<?xml version="1.0" encoding="UTF-8"?>\n' + writeXMLDocument(doc) + '\n'); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-conf b/sca-cpp/branches/lightweight-sca/modules/js/js-conf new file mode 100755 index 0000000000..aa29920619 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/js-conf @@ -0,0 +1,55 @@ +#!/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. + +# Add Javascript scripts and CSS to a server conf +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +cat >>$root/conf/httpd.conf <<EOF +# Generated by: js-conf $* +# Serve JavaScript scripts and CSS +Alias /ui-min.css $here/htdocs/ui-min.css +Alias /all-min.js $here/htdocs/all-min.js +Alias /proxy/ui-min.css $here/htdocs/ui-min.css +Alias /proxy/all-min.js $here/htdocs/all-min.js + +EOF + +cat >>$root/conf/pubauth.conf <<EOF +# Generated by: js-conf $* +<Location /ui-min.css> +AuthType None +Require all granted +</Location> +<Location /all-min.js> +AuthType None +Require all granted +</Location> +<Location /proxy/ui-min.css> +AuthType None +Require all granted +</Location> +<Location /proxy/all-min.js> +AuthType None +Require all granted +</Location> + +EOF + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp new file mode 100644 index 0000000000..ee0fa89b31 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp @@ -0,0 +1,55 @@ +/* + * 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$ */ + +/** + * Evaluate a JavaScript script. + */ + +#include <assert.h> +#include "stream.hpp" +#include "fstream.hpp" +#include "string.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace js { + +bool eval() { + ostringstream os; + for(;;) { + int c = cin.get(); + if (c == -1) + break; + os << (char)c; + } + failable<value> v = evalScript(str(os)); + assert(hasContent(v)); + cout << v; + return true; +} + +} +} + +int main() { + tuscany::js::eval(); + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp b/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp new file mode 100644 index 0000000000..a7e5597610 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Test JavaScript evaluation functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace js { + +bool testJSEval() { + failable<value> v = evalScript("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)"); + assert(hasContent(v)); + assert(content(v) == value(5)); + return true; +} + +} +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::js::testJSEval(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/js/json-test.js b/sca-cpp/branches/lightweight-sca/modules/js/json-test.js new file mode 100644 index 0000000000..fb53fa6347 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/json-test.js @@ -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. + */ + +/** + * Test JSON data encoding functions. + */ +function testJSON() { + var ad = mklist(mklist(attribute, "'city", "san francisco"), mklist(attribute, "'state", "ca")); + var ac = mklist(mklist(element, "'id", "1234"), mklist(attribute, "'balance", 1000)); + var cr = mklist(mklist(attribute, "'name", "jdoe"), cons(element, cons("'address", ad)), cons(element, cons("'account", ac))); + var c = mklist(cons(element, cons("'customer", cr))); + var s = json.writeJSON(c); + assert(car(s) == "{\"customer\": {\"@name\": \"jdoe\", \"address\": {\"@city\": \"san francisco\", \"@state\": \"ca\"}, \"account\": {\"id\": \"1234\", \"@balance\": 1000}}}"); + + var phones = mklist("408-1234", "650-1234"); + var l = mklist(mklist(element, "'phones", phones), mklist(element, "'lastName", "test\ttab"), mklist(attribute, "'firstName", "test1")); + var s2 = json.writeJSON(l); + assert(car(s2) == "{\"phones\": [\"408-1234\", \"650-1234\"], \"lastName\": \"test\\ttab\", \"@firstName\": \"test1\"}"); + + var r = json.readJSON(s2); + var w = json.writeJSON(r); + assert(car(w) == "{\"phones\": [\"408-1234\", \"650-1234\"], \"lastName\": \"test\\ttab\", \"@firstName\": \"test1\"}"); + + var l4 = mklist(mklist("'ns1:echoString", mklist("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), mklist("'text", "Hello World!"))); + var s4 = json.writeJSON(valuesToElements(l4)); + assert(car(s4) == "{\"ns1:echoString\": {\"@xmlns:ns1\": \"http://ws.apache.org/axis2/services/echo\", \"text\": \"Hello World!\"}}"); + + var r5 = elementsToValues(json.readJSON(s4)); + var s5 = json.writeJSON(valuesToElements(r5)); + assert(car(s5) == "{\"ns1:echoString\": {\"@xmlns:ns1\": \"http://ws.apache.org/axis2/services/echo\", \"text\": \"Hello World!\"}}"); + return true; +} + +testJSON(); + diff --git a/sca-cpp/branches/lightweight-sca/modules/js/util-test b/sca-cpp/branches/lightweight-sca/modules/js/util-test new file mode 100755 index 0000000000..c69f46c67c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/js/util-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. + +# Run Javascript util test cases +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` + +echo "Testing..." +cat htdocs/util.js htdocs/elemutil.js htdocs/jsonutil.js json-test.js | ./js-shell 2>/dev/null 1>&2 +rc=$? +if [ "$rc" = "0" ]; then + echo "OK" +fi + +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am new file mode 100644 index 0000000000..3e76a435c8 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am @@ -0,0 +1,58 @@ +# 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_OPENCL + +INCLUDES = -I${OPENCL_INCLUDE} + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/opencl + +dist_mod_SCRIPTS = opencl-conf +moddir = $(prefix)/modules/opencl + +EXTRA_DIST = domain-test.composite server-test.cl + +if DARWIN +OPENCL_FLAGS = -framework OpenCL +else +OPENCL_FLAGS = -L${OPENCL_LIB} -R${OPENCL_LIB} -lOpenCL +endif + +#mod_LTLIBRARIES = libmod_tuscany_opencl.la +#libmod_tuscany_opencl_la_SOURCES = mod-opencl.cpp +#libmod_tuscany_opencl_la_LDFLAGS = -lxml2 -lcurl -lmozjs -framework OpenCL +#noinst_DATA = libmod_tuscany_opencl${libsuffix} +#libmod_tuscany_opencl${libsuffix}: +# ln -s .libs/libmod_tuscany_opencl${libsuffix} + +opencl_test_SOURCES = opencl-test.cpp +opencl_test_LDFLAGS = ${OPENCL_FLAGS} + +opencl_shell_SOURCES = opencl-shell.cpp +opencl_shell_LDFLAGS = ${OPENCL_FLAGS} + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +dist_noinst_SCRIPTS = server-test +noinst_PROGRAMS = opencl-test client-test +mod_PROGRAMS = opencl-shell +TESTS = opencl-test + +endif diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/client-test.cpp new file mode 100644 index 0000000000..7af3cc73d2 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/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/opencl"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite new file mode 100644 index 0000000000..e69399d581 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite @@ -0,0 +1,31 @@ +<?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" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="opencl-test"> + <implementation.opencl kernel="server-test.cl"/> + <service name="test"> + <binding.http uri="opencl"/> + </service> + </component> + +</composite> diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp new file mode 100644 index 0000000000..b4b6c2845b --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp @@ -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$ */ + +#ifndef tuscany_opencl_driver_hpp +#define tuscany_opencl_driver_hpp + +/** + * OpenCL evaluator main driver loop. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "monad.hpp" +#include "../scheme/driver.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace opencl { + +const value evalDriverLoop(const OpenCLProgram& clprog, istream& in, ostream& out, const OpenCLContext& cl) { + scheme::promptForInput(scheme::evalInputPrompt, out); + value input = scheme::readValue(in); + if (isNil(input)) + return input; + const failable<value> output = evalKernel(createKernel(input, clprog), input, 1, value::String, 512, cl); + scheme::announceOutput(scheme::evalOutputPrompt, out); + scheme::userPrint(content(output), out); + return evalDriverLoop(clprog, in, out, cl); +} + +const bool evalDriverRun(const char* path, istream& in, ostream& out) { + OpenCLContext cl(OpenCLContext::DEFAULT); + scheme::setupDisplay(out); + ifstream is(path); + failable<OpenCLProgram> clprog = readProgram(path, is, cl); + if (!hasContent(clprog)) + return true; + evalDriverLoop(content(clprog), in, out, cl); + return true; +} + +} +} +#endif /* tuscany_opencl_driver_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp new file mode 100644 index 0000000000..5606b2f57a --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp @@ -0,0 +1,724 @@ +/* + * 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_opencl_eval_hpp +#define tuscany_opencl_eval_hpp + +/** + * OpenCL kernel evaluation logic. + */ +#ifdef IS_DARWIN +#include <OpenCL/opencl.h> +#else +#include <CL/cl.h> +#endif + +#include "list.hpp" +#include "value.hpp" +#include "perf.hpp" + +namespace tuscany { +namespace opencl { + +/** + * Convert an OpenCL error code to a string. + */ +const string clError(const cl_int e) { + ostringstream s; + s << "error " << e; + return str(s); +} + +/** + * OpenCL profiling counters. + */ +#ifdef WANT_MAINTAINER_OPENCL_PROF + +cl_ulong memtime = 0; +cl_ulong kernelqtime = 0; +cl_ulong kerneltime = 0; +cl_ulong preptime = 0; +cl_ulong evaltime = 0; + +/** + * Reset the OpenCL profiling counters. + */ +bool resetOpenCLCounters() { + memtime = kernelqtime = kerneltime = preptime = evaltime = 0; + return true; +} + +/** + * Print the OpenCL profiling counters. + */ +bool printOpenCLCounters(const long n) { + cout << " kernelq " << ((double)kernelqtime / 1000000.0) / (double)n << " ms kernel " << ((double)kerneltime / 1000000.0) / (double)n << " ms memory " << ((double)memtime / 1000000.0) / (double)n << " ms prep " << ((double)preptime / 1000000.0) / (double)n << " ms eval " << ((double)evaltime / 1000000.0) / (double)n << " ms"; + return true; +} + +/** + * Profile a memory event. + */ +failable<cl_ulong> profileMemEvent(const cl_event evt) { + cl_ulong start = 0; + const cl_int serr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start, NULL); + if (serr != CL_SUCCESS) + return mkfailure<cl_ulong>("Couldn't profile memory event start: " + clError(serr)); + cl_ulong end = 0; + const cl_int eerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end, NULL); + if (eerr != CL_SUCCESS) + return mkfailure<cl_ulong>("Couldn't profile memory event end: " + clError(eerr)); + const cl_ulong t = end - start; + memtime += t; + return t; +} + +/** + * Profile a kernel event. + */ +failable<cl_ulong> profileKernelEvent(const cl_event evt) { + cl_ulong queued = 0; + const cl_int qerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_QUEUED, sizeof(cl_ulong), &queued, NULL); + if (qerr != CL_SUCCESS) + return mkfailure<cl_ulong>("Couldn't profile kernel event queue: " + clError(qerr)); + cl_ulong start = 0; + const cl_int serr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start, NULL); + if (serr != CL_SUCCESS) + return mkfailure<cl_ulong>("Couldn't profile kernel event start: " + clError(serr)); + cl_ulong end = 0; + const cl_int eerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end, NULL); + if (eerr != CL_SUCCESS) + return mkfailure<cl_ulong>("Couldn't profile kernel event end: " + clError(eerr)); + const cl_ulong q = start - queued; + kernelqtime += q; + const cl_ulong t = end - start; + kerneltime += t; + return t; +} + +/** + * Profile an array of memory events. + */ +failable<cl_ulong> profileMemEvents(const cl_uint n, const cl_event* evt) { + if (n == 0) + return 0; + const failable<cl_ulong> t = profileMemEvent(*evt); + if (!hasContent(t)) + return t; + const failable<cl_ulong> r = profileMemEvents(n - 1, evt + 1); + if (!hasContent(r)) + return r; + return content(t) + content(r); +} + +#else + +#define resetOpenCLCounters() +#define printOpenCLCounters(n) + +#endif + +class OpenCLContext; +class OpenCLProgram; +class OpenCLKernel; +class OpenCLBuffer; + +/** + * Represent an OpenCL context. + */ +class OpenCLContext { +public: +#define OPENCL_MAX_DEVICES 64 + + enum DeviceType { + DEFAULT = 0, CPU = 1, GPU = 2 + }; + + OpenCLContext(const OpenCLContext::DeviceType devtype) : dev(OpenCLContext::DEFAULT), ndevs(0), ctx(0) { + debug("opencl::OpenCLContext"); + for (int i = 0; i < OPENCL_MAX_DEVICES; i++) + cq[i] = 0; + + // Get the available platforms + cl_uint nplatforms; + cl_platform_id platforms[16]; + const cl_int gperr = clGetPlatformIDs(16, platforms, &nplatforms); + if(nplatforms == 0 || gperr != CL_SUCCESS) { + mkfailure<bool>("Couldn't get OpenCL platforms: " + clError(gperr)); + return; + } + for(cl_uint i = 0; i < nplatforms; ++i) { + char vendor[256]; + const cl_int gverr = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL); + if(gverr != CL_SUCCESS) { + mkfailure<bool>("Couldn't get OpenCL platform: " + clError(gverr)); + return; + } + debug(vendor, "opencl::OpenCLContext::vendor"); + } + + // Get the available devices of the requested type + if (devtype == OpenCLContext::DEFAULT || devtype == OpenCLContext::GPU) { + for(cl_uint i = 0; i < nplatforms; ++i) { + const cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_GPU, OPENCL_MAX_DEVICES, devid, &ndevs); + if (err == CL_SUCCESS) { + debug(ndevs, "opencl::OpenCLContext::gcpus"); + dev = OpenCLContext::GPU; + break; + } + } + } + if ((devtype == OpenCLContext::DEFAULT && ndevs == 0) || devtype == OpenCLContext::CPU) { + for(cl_uint i = 0; i < nplatforms; ++i) { + const cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_CPU, OPENCL_MAX_DEVICES, devid, &ndevs); + if (err == CL_SUCCESS) { + debug(ndevs, "opencl::OpenCLContext::ncpus"); + dev = OpenCLContext::CPU; + break; + } + } + } + if (ndevs == 0) + return; + + // Initialize OpenCL context and command queues + cl_int ccerr; + ctx = clCreateContext(0, ndevs, devid, NULL, NULL, &ccerr); + if(!ctx || ccerr != CL_SUCCESS) { + mkfailure<bool>("Couldn't create OpenCL context: " + clError(ccerr)); + return; + } + + for (cl_uint i = 0; i < ndevs; i++) { + cl_int cqerr; +#ifdef WANT_MAINTAINER_OPENCL_PROF + cq[i] = clCreateCommandQueue(ctx, devid[i], CL_QUEUE_PROFILING_ENABLE, &cqerr); +#else + cq[i] = clCreateCommandQueue(ctx, devid[i], 0, &cqerr); +#endif + if (!cq[i] || cqerr != CL_SUCCESS) { + mkfailure<bool>("Couldn't create OpenCL command queue: " + clError(cqerr)); + return; + } + } + } + + ~OpenCLContext() { + for (cl_uint i = 0; i < ndevs; i++) { + if (cq[i] != 0) + clReleaseCommandQueue(cq[i]); + } + if (ctx != 0) + clReleaseContext(ctx); + } + +private: + OpenCLContext::DeviceType dev; + cl_uint ndevs; + cl_device_id devid[OPENCL_MAX_DEVICES]; + cl_context ctx; + cl_command_queue cq[OPENCL_MAX_DEVICES]; + + friend const cl_uint devices(const OpenCLContext& cl); + friend const cl_command_queue commandq(const OpenCLContext& cl); + friend const failable<OpenCLBuffer> readOnlyBuffer(const size_t size, const void* p, const OpenCLContext& cl, cl_command_queue cq); + friend const failable<OpenCLBuffer> writeOnlyBuffer(const size_t size, const OpenCLContext& cl); + friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl); + friend const failable<OpenCLProgram> readProgram(const string& path, istream& is, const OpenCLContext& cl); +}; + +/** + * Return the number of computing devices available in a context. + */ +const cl_uint devices(const OpenCLContext& cl) { + return cl.ndevs; +} + +/** + * Return a command queue from a context. + */ +const cl_command_queue commandq(const OpenCLContext& cl) { + return cl.cq[0]; +} + +/** + * Represents an OpenCL program. + */ +class OpenCLProgram { +public: + OpenCLProgram() : prog(0) { + } + + OpenCLProgram(const cl_program prog) : prog(prog) { + } + + OpenCLProgram(const OpenCLProgram& c) : prog(c.prog) { + const cl_int rperr = clRetainProgram(prog); + if (rperr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't retain OpenCL program: ") + clError(rperr)); + } + + ~OpenCLProgram() { + if (!prog) + return; + const cl_int rperr = clReleaseProgram(prog); + if (rperr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't release OpenCL program: ") + clError(rperr)); + } + +private: + const cl_program prog; + + friend const failable<OpenCLKernel> createKernel(const value& expr, const OpenCLProgram& clprog); + friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl); +}; + +/** + * Represents an OpenCL kernel. + */ +class OpenCLKernel { +public: + OpenCLKernel() : k(0) { + } + + OpenCLKernel(const cl_kernel k) : k(k) { + } + + OpenCLKernel(const OpenCLKernel& c) : k(c.k) { + const cl_int rkerr = clRetainKernel(k); + if (rkerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't retain OpenCL kernel: ") + clError(rkerr)); + } + + ~OpenCLKernel() { + if (!k) + return; + const cl_int rkerr = clReleaseKernel(k); + if (rkerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't release OpenCL kernel: ") + clError(rkerr)); + } + +private: + const cl_kernel k; + + friend const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel); + friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl); +}; + +/** + * Represents an OpenCL buffer. + */ +class OpenCLBuffer { +public: + OpenCLBuffer() : mem(0), evt(0) { + } + + OpenCLBuffer(const cl_mem mem, const cl_event evt) : mem(mem), evt(evt) { + } + + OpenCLBuffer(const OpenCLBuffer& c) : mem(c.mem), evt(c.evt) { + if (mem != 0) { + const cl_int rmerr = clRetainMemObject(mem); + if (rmerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't retain OpenCL buffer: ") + clError(rmerr)); + } + if (evt != 0) { + const cl_int reerr = clRetainEvent(evt); + if (reerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't retain OpenCL event: ") + clError(reerr)); + } + } + + ~OpenCLBuffer() { + if (mem != 0) { + const cl_int rmerr = clReleaseMemObject(mem); + if (rmerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't release OpenCL buffer: ") + clError(rmerr)); + } + if (evt != 0) { + const cl_int reerr = clReleaseEvent(evt); + if (reerr != CL_SUCCESS) + mkfailure<bool>(string("Couldn't release OpenCL event: ") + clError(reerr)); + } + } + +private: + const cl_mem mem; + const cl_event evt; + + friend const cl_uint writeBufferEvents(const list<OpenCLBuffer>& buf, cl_event* evt); + friend const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel); + friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl); +}; + +/** + * Return a read-only memory buffer. + */ +const failable<OpenCLBuffer> readOnlyBuffer(const size_t size, const void* p, const OpenCLContext& cl, const cl_command_queue cq) { + if (cl.dev == OpenCLContext::CPU) { + cl_int err; + const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, size, const_cast<void*>(p), &err); + if (!buf || err != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't map OpenCL host memory: ") + clError(err)); + return OpenCLBuffer(buf, 0); + } + cl_int berr; + const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_READ_ONLY, size, NULL, &berr); + if (!buf || berr != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't allocate OpenCL device memory: ") + clError(berr)); + cl_event wevt; + const cl_int werr = clEnqueueWriteBuffer(cq, buf, CL_FALSE, 0, size, p, 0, NULL, &wevt); + if (werr != CL_SUCCESS) { + clReleaseMemObject(buf); + return mkfailure<OpenCLBuffer>(string("Couldn't enqueue write to device memory: ") + clError(werr)); + } + return OpenCLBuffer(buf, wevt); +} + +/** + * Fill an array of write events for a given list of buffers. + */ +const cl_uint writeBufferEvents(const list<OpenCLBuffer>& buf, cl_event* evt) { + if (isNil(buf)) + return 0; + const cl_event e = car(buf).evt; + if (e == 0) + return writeBufferEvents(cdr(buf), evt); + *evt = e; + return 1 + writeBufferEvents(cdr(buf), evt + 1); +} + +/** + * Return a write-only memory buffer. + */ +const failable<OpenCLBuffer> writeOnlyBuffer(const size_t size, const OpenCLContext& cl) { + if (cl.dev == OpenCLContext::CPU) { + cl_int err; + const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, size, NULL, &err); + if (!buf || err != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't map OpenCL host memory: ") + clError(err)); + return OpenCLBuffer(buf, 0); + } + cl_int err; + const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_WRITE_ONLY, size, NULL, &err); + if (!buf || err != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't allocate OpenCL device memory: ") + clError(err)); + return OpenCLBuffer(buf, 0); +} + +/** + * Convert a value to a kernel arg. + */ +const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel) { + if (!hasContent(buf)) + return buf; + if (p != NULL) { + const cl_int err = clSetKernelArg(kernel.k, (cl_uint)i, size, p); + if (err != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't set OpenCL simple kernel arg: ") + clError(err)); + return buf; + } + const OpenCLBuffer b = content(buf); + const cl_int err = clSetKernelArg(kernel.k, i, sizeof(cl_mem), &b.mem); + if (err != CL_SUCCESS) + return mkfailure<OpenCLBuffer>(string("Couldn't set OpenCL buffer kernel arg: ") + clError(err)); + return buf; +} + +const failable<OpenCLBuffer> valueToKernelArg(const value& v, const cl_uint i, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) { + switch (type(v)) { + case value::Symbol: { + const string s = string("'") + v; + return valueToKernelArg(i, 0, NULL, readOnlyBuffer(length(s) + 1, c_str(s), cl, cq), kernel); + } + case value::String: { + const string s = (string)v; + return valueToKernelArg(i, 0, NULL, readOnlyBuffer(length(s) + 1, c_str(s), cl, cq), kernel); + } + case value::Number: { + const cl_float d = (cl_float)((double)v); + return valueToKernelArg(i, sizeof(cl_float), &d, OpenCLBuffer(), kernel); + } + case value::Bool: { + const cl_int b = (cl_int)((bool)v); + return valueToKernelArg(i, sizeof(cl_int), &b, OpenCLBuffer(), kernel); + } + default: { + return valueToKernelArg(i, sizeof(cl_mem), NULL, readOnlyBuffer(sizeof(value), &v, cl, cq), kernel); + } + } +} + +/** + * Convert a list of values to kernel args. + */ +const failable<list<OpenCLBuffer>> valuesToKernelArgsListHelper(const list<value>& v, const cl_uint i, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) { + if (isNil(v)) + return list<OpenCLBuffer>(); + const failable<OpenCLBuffer> a = valueToKernelArg(car(v), i, kernel, cl, cq); + if (!hasContent(a)) + return mkfailure<list<OpenCLBuffer>>(a); + const failable<list<OpenCLBuffer>> al = valuesToKernelArgsListHelper(cdr(v), i + 1, kernel, cl, cq); + if (!hasContent(al)) + return al; + return cons<OpenCLBuffer>(content(a), content(al)); +} + +const failable<list<OpenCLBuffer>> valuesToKernelArgs(const list<value>& v, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) { + return valuesToKernelArgsListHelper(v, 0, kernel, cl, cq); +} + +/** + * Convert a kernel result to a value. + */ +const value kernelResultToValue(const void* p, const value::ValueType type) { + switch(type) { + case value::Symbol: { + const char* s = static_cast<const char*>(p); + const size_t l = strlen(s); + if (l != 0 && *s == '\'') + return value(s + 1); + return value(s); + } + case value::String: { + const char* s = static_cast<const char*>(p); + const size_t l = strlen(s); + if (l != 0 && *s == '\'') + return value(s + 1); + return value(string(s, l)); + } + case value::Number: + return (double)(*(static_cast<const cl_float*>(p))); + case value::Bool: + return (bool)(*(static_cast<const cl_int*>(p))); + default: + return *(static_cast<const value*>(p)); + } +} + +/** + * Return the value type corresponding to a C99 type name. + */ +const value::ValueType valueType(const string& t) { + if (t == "float") + return value::Number; + if (t == "int") + return value::Bool; + if (t == "char") + return value::String; + return value::Nil; +} + +/** + * Return the size of a C99 type corresponding to a value type. + */ +const size_t valueSize(const value::ValueType type) { + switch(type) { + case value::Number: + return sizeof(cl_float); + case value::Bool: + return sizeof(cl_int); + case value::Symbol: + return sizeof(cl_char); + case value::String: + return sizeof(cl_char); + default: + return sizeof(value); + } +} + +/** + * Return the result type of a kernel. + */ +class OpenCLResultType { +public: + OpenCLResultType(const value::ValueType type, const size_t n, const size_t size) : type(type), n(n), size(size) {} + const value::ValueType type; + const size_t n; + const size_t size; +}; + +const OpenCLResultType kernelResultType(const string& fn, value::ValueType type, const size_t n) { + if (type != value::Nil) + return OpenCLResultType(type, n, valueSize(type)); + const string s = car(tokenize("_", fn)); + const size_t d = find_first_of(s, "0123456789"); + if (d == length(s)) { + const value::ValueType vt = valueType(s); + return OpenCLResultType(vt, 1, valueSize(vt)); + } + const value::ValueType vt = valueType(substr(s, 0, d)); + return OpenCLResultType(vt, atoi(c_str(substr(s, d))), valueSize(vt)); +} + +/** + * Create the kernel implementing an expression. + */ +const failable<OpenCLKernel> createKernel(const value& expr, const OpenCLProgram& clprog) { + + // Create an OpenCL kernel for the requested function + const value fn = car<value>(expr); + cl_int ckerr; + const cl_kernel k = clCreateKernel(clprog.prog, c_str(fn), &ckerr); + if (k == NULL || ckerr != CL_SUCCESS) { + + // The start, stop, and restart functions are optional + //if (fn == "start" || fn == "stop") + //return value(lambda<value(const list<value>&)>()); + + return mkfailure<OpenCLKernel>(string("Couldn't find function: ") + car<value>(expr) + " : " + clError(ckerr)); + } + return OpenCLKernel(k); +} + +/** + * Evaluate an expression implemented by a kernel. + */ +const failable<value> evalKernel(const failable<OpenCLKernel>& fkernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl) { + +#ifdef WANT_MAINTAINER_OPENCL_PROF + const cl_uint estart = (cl_uint)timens(); + const cl_uint pstart = estart; +#endif + + if (!hasContent(fkernel)) + return mkfailure<value>(fkernel); + const OpenCLKernel kernel = content(fkernel); + + // Get a command queue for the specified device type + const cl_command_queue cq = commandq(cl); + + // Set the kernel input args + const failable<list<OpenCLBuffer>> args = valuesToKernelArgs(cdr<value>(expr), kernel, cl, cq); + if (!hasContent(args)) { + return mkfailure<value>(args); + } + + // Allocate result buffer in device memory + const value fn = car<value>(expr); + const OpenCLResultType rtype = kernelResultType(fn, type, n); + const size_t rsize = rtype.n * rtype.size; + const failable<OpenCLBuffer> rbuf = writeOnlyBuffer(rsize, cl); + if (!hasContent(rbuf)) + return mkfailure<value>(rbuf); + + // Set it as a kernel output arg + const cl_mem rmem = content(rbuf).mem; + const failable<OpenCLBuffer> rarg = valueToKernelArg((cl_uint)length(cdr<value>(expr)), sizeof(cl_mem), &rmem, rbuf, kernel); + if (!hasContent(rarg)) + return mkfailure<value>(rarg); + + // Enqueue the kernel, to be executed after all the writes complete + cl_event wevt[32]; + const cl_uint nwevt = writeBufferEvents(content(args), wevt); + cl_event kevt; + const cl_int qerr = clEnqueueNDRangeKernel(cq, kernel.k, 1, NULL, &gwsize, NULL, nwevt, nwevt != 0? wevt : NULL, &kevt); + if (qerr != CL_SUCCESS) + return mkfailure<value>(string("Couldn't enqueue kernel task: ") + clError(qerr)); + + // Enqueue result buffer read, to be executed after the kernel completes + char res[rsize]; + cl_event revt; + const cl_int rerr = clEnqueueReadBuffer(cq, rmem, CL_FALSE, 0, rsize, res, 1, &kevt, &revt); + if (rerr != CL_SUCCESS) { + clReleaseEvent(kevt); + return mkfailure<value>(string("Couldn't read from OpenCL device memory: ") + clError(rerr)); + } + +#ifdef WANT_MAINTAINER_OPENCL_PROF + const cl_uint pend = (cl_uint)timens(); + preptime += (pend - pstart); +#endif + + // Wait for completion + const cl_int werr = clWaitForEvents(1, &revt); + if (werr != CL_SUCCESS) { + clReleaseEvent(revt); + clReleaseEvent(kevt); + return mkfailure<value>(string("Couldn't wait for kernel completion: ") + clError(werr)); + } + +#ifdef WANT_MAINTAINER_OPENCL_PROF + profileMemEvents(nwevt, wevt); + profileKernelEvent(kevt); + profileMemEvent(revt); +#endif + + // Convert the result to a value + const value v = kernelResultToValue(res, rtype.type); + + // Release OpenCL resources + clReleaseEvent(revt); + clReleaseEvent(kevt); + +#ifdef WANT_MAINTAINER_OPENCL_PROF + const cl_uint eend = (cl_uint)timens(); + evaltime += (eend - estart); +#endif + + return v; +} + +const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const OpenCLContext& cl) { + return evalKernel(kernel, expr, 1, value::Nil, 0, cl); +} + +/** + * Read an opencl program from an input stream. + */ +const failable<OpenCLProgram> readProgram(const string& path, istream& is, const OpenCLContext& cl) { + + // Read the program source + const list<string> ls = streamList(is); + ostringstream os; + write(ls, os); + const char* cs = c_str(str(os)); + + // Create the OpenCL program + cl_int cperr; + const cl_program prog = clCreateProgramWithSource(cl.ctx, 1, (const char **)&cs, NULL, &cperr); + if (!prog || cperr != CL_SUCCESS) + return mkfailure<OpenCLProgram>(string("Couldn't create OpenCL program from source: ") + path + " : " + clError(cperr)); + + // Built it + const cl_int bperr = clBuildProgram(prog, 0, NULL, NULL, NULL, NULL); + if(bperr != CL_SUCCESS) { + size_t l; + char b[2048]; + clGetProgramBuildInfo(prog, cl.devid[0], CL_PROGRAM_BUILD_LOG, sizeof(b), b, &l); + return mkfailure<OpenCLProgram>(string("Couldn't build OpenCL program: ") + path + " : " + clError(bperr) + "\n" + string(b)); + } + return OpenCLProgram(prog); +} + +/** + * Evaluate an expression against an OpenCL program provided as an input stream. + */ +const failable<value> evalKernel(const value& expr, istream& is, const OpenCLContext& cl) { + failable<OpenCLProgram> clprog = readProgram("program.cl", is, cl); + if (!hasContent(clprog)) + return mkfailure<value>(clprog); + return evalKernel(createKernel(expr, content(clprog)), expr, 1, value::Nil, 0, cl); +} + +} +} +#endif /* tuscany_opencl_eval_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf new file mode 100755 index 0000000000..1ba2c336a3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf @@ -0,0 +1,37 @@ +#!/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 an OpenCL server conf +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +cat >>$root/conf/modules.conf <<EOF +# Generated by: opencl-conf $* +# Support for OpenCL SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_opencl$libsuffix + +EOF diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp new file mode 100644 index 0000000000..1dfeaeea1d --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-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$ */ + +/** + * OpenCL 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: opencl-shell <kernel.cl>" << tuscany::endl; + return 1; + } + tuscany::opencl::evalDriverRun(argv[1], tuscany::cin, tuscany::cout); + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp new file mode 100644 index 0000000000..17bc5ccfa6 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp @@ -0,0 +1,332 @@ +/* + * 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 OpenCL kernel evaluator. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "driver.hpp" +#include "parallel.hpp" +#include "perf.hpp" + +namespace tuscany { +namespace opencl { + +const string testFloatsCPU = + "kernel void add(const float x, const float y, global float* r) {\n" + " float l_x;\n" + " float l_y;\n" + " l_x = x;\n" + " l_y = y;\n" + " *r = l_x + l_y;\n" + "}\n" + "kernel void float_add(const float x, const float y, global float* r) {\n" + " add(x, y, r);\n" + "}\n"; + +const string testFloatsGPU = + "kernel void add(const float x, const float y, global float* r) {\n" + " local float l_x;\n" + " local float l_y;\n" + " l_x = x;\n" + " l_y = y;\n" + " barrier(CLK_LOCAL_MEM_FENCE);\n" + " *r = l_x + l_y;\n" + "}\n" + "kernel void float_add(const float x, const float y, global float* r) {\n" + " add(x, y, r);\n" + "}\n"; + +const string testBoolsCPU = + "kernel void int_or(const int x, const int y, global int* r) {\n" + " int l_x;\n" + " int l_y;\n" + " l_x = x;\n" + " l_y = y;\n" + " *r = l_x || l_y;\n" + "}\n"; + +const string testBoolsGPU = + "kernel void int_or(const int x, const int y, global int* r) {\n" + " local int l_x;\n" + " local int l_y;\n" + " l_x = x;\n" + " l_y = y;\n" + " barrier(CLK_LOCAL_MEM_FENCE);\n" + " *r = l_x || l_y;\n" + "}\n"; + +const string testCharsCPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " for (int i = 0; i < ixl; i++)\n" + " r[i] = x[i];\n" + " for (int i = 0; i < iyl; i++)\n" + " r[ixl + i] = y[i];\n" + " r[ixl + iyl] = '\\0';\n" + "}\n" + "kernel void char128_add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " add(xl, x, yl, y, r);\n" + "}\n"; + +const string testCharsCopyGPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " local char l_x[128];\n" + " local char l_y[128];\n" + " event_t re = async_work_group_copy(l_x, x, ixl, 0);\n" + " async_work_group_copy(l_y, y, iyl, re);\n" + " wait_group_events(1, &re);\n" + " local char l_r[128];\n" + " for (int i = 0; i < ixl; i++)\n" + " l_r[i] = l_x[i];\n" + " for (int i = 0; i < iyl; i++)\n" + " l_r[ixl + i] = l_y[i];\n" + " l_r[ixl + iyl] = '\\0';\n" + " event_t we = async_work_group_copy(r, l_r, ixl + iyl + 1, 0);\n" + " wait_group_events(1, &we);\n" + "}\n" + "kernel void char128(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " add(xl, x, yl, y, r);\n" + "}\n"; + +const string testCharsGPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " for (int i = 0; i < ixl; i++)\n" + " r[i] = x[i];\n" + " for (int i = 0; i < iyl; i++)\n" + " r[ixl + i] = y[i];\n" + " r[ixl + iyl] = '\\0';\n" + "}\n" + "kernel void char128(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " add(xl, x, yl, y, r);\n" + "}\n"; + +const string testCharsParallelCPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int i = get_global_id(0);\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " r[i] = i < ixl? x[i] : i < ixl + iyl? y[i - ixl] : '\\0';\n" + "}\n"; + +const string testCharsParallelCopyGPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int i = get_global_id(0);\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " local char l_x[128];\n" + " local char l_y[128];\n" + " event_t re = async_work_group_copy(l_x, x, ixl, 0);\n" + " async_work_group_copy(l_y, y, iyl, re);\n" + " wait_group_events(1, &re);\n" + " local char l_r[128];\n" + " l_r[i] = i < ixl? l_x[i] : i < ixl + iyl? l_y[i - ixl] : '\\0';\n" + " event_t we = async_work_group_copy(r, l_r, ixl + iyl + 1, 0);\n" + " wait_group_events(1, &we);\n" + "}\n"; + +const string testCharsParallelGPU = + "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n" + " const int i = get_global_id(0);\n" + " const int ixl = (int)xl;\n" + " const int iyl = (int)yl;\n" + " r[i] = i < ixl? x[i] : i < ixl + iyl? y[i - ixl] : '\\0';\n" + "}\n"; + +bool testTaskParallel(const OpenCLContext::DeviceType dev) { + gc_scoped_pool pool; + OpenCLContext cl(dev); + if (!devices(cl) != 0) + return true; + + { + istringstream is(dev == OpenCLContext::CPU? testFloatsCPU : testFloatsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + const value exp = mklist<value>("float_add", 2, 3); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl); + assert(hasContent(r)); + assert(content(r) == value(5)); + } + if (true) return true; + { + istringstream is(dev == OpenCLContext::CPU? testFloatsCPU : testFloatsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + const value exp = mklist<value>("add", 2, 3); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 1, value::Number, 1, cl); + assert(hasContent(r)); + assert(content(r) == value(5)); + } + { + istringstream is(dev == OpenCLContext::CPU? testBoolsCPU : testBoolsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + const value exp = mklist<value>("int_or", true, false); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl); + assert(hasContent(r)); + assert(content(r) == value(true)); + + const value exp2 = mklist<value>("int_or", false, false); + const failable<value> r2 = evalKernel(createKernel(exp2, content(clprog)), exp2, cl); + assert(hasContent(r2)); + assert(content(r2) == value(false)); + } + { + istringstream is(dev == OpenCLContext::CPU? testCharsCPU : testCharsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + const value exp = mklist<value>("char128", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World ")); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl); + assert(hasContent(r)); + assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World "))); + } + { + istringstream is(dev == OpenCLContext::CPU? testCharsCPU : testCharsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + const value exp = mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World ")); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 1, value::String, 128, cl); + assert(hasContent(r)); + assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World "))); + } + + return true; +} + +struct evalTaskParallelLoop { + evalTaskParallelLoop(const OpenCLContext& cl, const OpenCLProgram& clprog) : cl(cl), clprog(clprog), exp(mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "))) { + } + const bool operator()() const { + const failable<value> r = evalKernel(createKernel(exp, clprog), exp, 1, value::String, 128, cl); + assert(hasContent(r)); + assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World "))); + return true; + } + + const OpenCLContext& cl; + const OpenCLProgram& clprog; + const value exp; +}; + +const bool testTaskParallelPerf(const OpenCLContext::DeviceType dev, const bool copy) { + gc_scoped_pool pool; + OpenCLContext cl(dev); + if (!devices(cl) != 0) + return true; + + istringstream is(dev == OpenCLContext::CPU? testCharsCPU : copy? testCharsCopyGPU : testCharsGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + resetOpenCLCounters(); + const lambda<bool()> el = evalTaskParallelLoop(cl, content(clprog)); + cout << "OpenCL task-parallel eval " << (dev == OpenCLContext::CPU? "CPU" : "GPU") << (copy? " copy" : "") << " test " << time(el, 5, 500) << " ms"; + printOpenCLCounters(500); + cout << endl; + return true; +} + +bool testDataParallel(const OpenCLContext::DeviceType dev) { + gc_scoped_pool pool; + OpenCLContext cl(dev); + if (!devices(cl) != 0) + return true; + + { + istringstream is(dev == OpenCLContext::CPU? testCharsParallelCPU : testCharsParallelGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + const value exp = mklist<value>("add", 6, string("Hello "), 5, string("World")); + const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 121, value::String, 128, cl); + assert(hasContent(r)); + assert(content(r) == value(string("Hello World"))); + } + + return true; +} + +struct evalDataParallelLoop { + evalDataParallelLoop(const OpenCLContext& cl, const OpenCLProgram& clprog) : cl(cl), clprog(clprog), exp(mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "))) { + } + const bool operator()() const { + const failable<value> r = evalKernel(createKernel(exp, clprog), exp, 121, value::String, 128, cl); + assert(hasContent(r)); + assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World "))); + return true; + } + + const OpenCLContext& cl; + const OpenCLProgram& clprog; + const value exp; +}; + +const bool testDataParallelPerf(const OpenCLContext::DeviceType dev, const bool copy) { + gc_scoped_pool pool; + OpenCLContext cl(dev); + if (!devices(cl) != 0) + return true; + + istringstream is(dev == OpenCLContext::CPU? testCharsParallelCPU : copy? testCharsParallelCopyGPU : testCharsParallelGPU); + failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl); + assert(hasContent(clprog)); + + resetOpenCLCounters(); + const lambda<bool()> el = evalDataParallelLoop(cl, content(clprog)); + cout << "OpenCL data-parallel eval " << (dev == OpenCLContext::CPU? "CPU" : "GPU") << (copy? " copy" : "") << " test " << time(el, 5, 500) << " ms"; + printOpenCLCounters(500); + cout << endl; + return true; +} + +} +} + +int main() { + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::opencl::testTaskParallel(tuscany::opencl::OpenCLContext::CPU); + tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::CPU, false); + tuscany::opencl::testDataParallel(tuscany::opencl::OpenCLContext::CPU); + tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::CPU, false); + + tuscany::opencl::testTaskParallel(tuscany::opencl::OpenCLContext::GPU); + tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::GPU, false); + tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::GPU, true); + tuscany::opencl::testDataParallel(tuscany::opencl::OpenCLContext::GPU); + tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::GPU, false); + tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::GPU, true); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/server-test b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test new file mode 100755 index 0000000000..c103c45487 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/server-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. + +# Setup +rm -rf tmp +../http/httpd-conf tmp localhost 8090 ../server/htdocs +../server/server-conf tmp +./opencl-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 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl new file mode 100644 index 0000000000..de5c2d1b1e --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl @@ -0,0 +1,17 @@ +# 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. + diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am new file mode 100644 index 0000000000..54a9b46e4f --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am @@ -0,0 +1,27 @@ +# 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. + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/scdl + +scdl_test_SOURCES = scdl-test.cpp +scdl_test_LDFLAGS = -lxml2 + +EXTRA_DIST = test.composite + +noinst_PROGRAMS = scdl-test +TESTS = scdl-test diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp new file mode 100644 index 0000000000..4c10f515df --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp @@ -0,0 +1,125 @@ +/* + * 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(implementationType(impl) == "implementation.scheme"); + assert(attributeValue("script", impl) == string("store.scm")); + + 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) == "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) == "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::gc_scoped_pool p; + 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/lightweight-sca/modules/scdl/scdl.hpp b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp new file mode 100644 index 0000000000..7cf43e3b14 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp @@ -0,0 +1,221 @@ +/* + * 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 a list of service promotions in a composite. + */ +const list<value> promotions(const value& l) { + const list<value> cs = elementChildren("composite", l); + if (isNil(cs)) + return cs; + return elementChildren("service", car(cs)); +} + +/** + * Returns the target or a service promotion. + */ +const value promote(const value& l) { + return attributeValue("promote", l); +} + +/** + * 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 true if a reference is declared as wired by impl. + */ +const bool wiredByImpl(const value& l) { + return attributeValue("wiredByImpl", l) == string("true"); +} + +/** + * 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 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 the target of a reference. + */ +const value bindingsTarget(const list<value>& l) { + if (isNil(l)) + return value(); + const value u = uri(car(l)); + if (!isNil(u)) + return u; + return bindingsTarget(cdr(l)); +} + +const value target(const value& l) { + const value target = attributeValue("target", l); + if (!isNil(target)) + return target; + return bindingsTarget(bindings(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 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); +} + +/** + * Returns the absolute path of a resource in a contribution. + */ +const string resourcePath(const string& contrib, const string& path) { + return c_str(path)[0] == '/'? path : contrib + path; +} + +} +} + +#endif /* tuscany_scdl_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite b/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite new file mode 100644 index 0000000000..5d8c5d3b88 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite @@ -0,0 +1,63 @@ +<?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" + targetNamespace="http://store" + name="store"> + + <component name="Store"> + <implementation.scheme script="store.scm"/> + <service name="Widget"> + <binding.http uri="/store"/> + </service> + <reference name="catalog" target="Catalog"> + <binding.jsonrpc/> + </reference> + <reference name="shoppingCart" target="ShoppingCart/Cart"> + <binding.atom/> + </reference> + <reference name="shoppingTotal" target="ShoppingCart/Total"> + <binding.jsonrpc/> + </reference> + </component> + + <component name="Catalog"> + <implementation.scheme script="fruits-catalog.scm"/> + <property name="currencyCode">USD</property> + <service name="Catalog"> + <binding.jsonrpc/> + </service> + <reference name="currencyConverter" target="CurrencyConverter"/> + </component> + + <component name="ShoppingCart"> + <implementation.scheme script="shopping-cart.scm"/> + <service name="Cart"> + <binding.atom uri="/ShoppingCart"/> + </service> + <service name="Total"> + <binding.jsonrpc/> + </service> + </component> + + <component name="CurrencyConverter"> + <implementation.scheme script="currency-converter.scm"/> + </component> + +</composite> diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test b/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test new file mode 100755 index 0000000000..9f277cf15a --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test @@ -0,0 +1,23 @@ +#!/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. + +# Validate test composite +../../kernel/xsd-test ../../xsd/tuscany-sca-1.1.xsd test.composite 2>/dev/null +rc=$? +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am new file mode 100644 index 0000000000..130fe14303 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am @@ -0,0 +1,47 @@ +# 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. + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/scheme + +moddir = $(prefix)/modules/scheme + +scheme_test_SOURCES = scheme-test.cpp + +scheme_shell_SOURCES = scheme-shell.cpp + +value_element_SOURCES = value-element.cpp +value_element_LDFLAGS = + +element_value_SOURCES = element-value.cpp +element_value_LDFLAGS = + +xml_value_SOURCES = xml-value.cpp +xml_value_LDFLAGS = -lxml2 + +value_xml_SOURCES = value-xml.cpp +value_xml_LDFLAGS = -lxml2 + +json_value_SOURCES = json-value.cpp +json_value_LDFLAGS = -lmozjs + +value_json_SOURCES = value-json.cpp +value_json_LDFLAGS = -lmozjs + +noinst_PROGRAMS = scheme-test +mod_PROGRAMS = scheme-shell element-value value-element xml-value value-xml json-value value-json +TESTS = scheme-test diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp new file mode 100644 index 0000000000..112c226ed1 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/scheme/element-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp new file mode 100644 index 0000000000..8a443dbdb2 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Convert a scheme value representing an element to a value. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int elementValue() { + const value v = elementsToValues(readValue(cin)); + cout << writeValue(v); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::elementValue(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp new file mode 100644 index 0000000000..303a37cb3c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp @@ -0,0 +1,182 @@ +/* + * 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)))) { + if (isNil(cdr(cdr(exps)))) + return value(); + 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/lightweight-sca/modules/scheme/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/eval.hpp new file mode 100644 index 0000000000..34d1a7bc17 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/scheme/io.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp new file mode 100644 index 0000000000..8f9d70e7fe --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp @@ -0,0 +1,239 @@ +/* + * 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 failable<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 failable<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 mkfailure<value>(); + logStream() << "Illegal lexical syntax '" << firstChar << "'" << endl; + return readToken(in); +} + +const failable<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 failable<value> ftoken = readToken(in); + if (!hasContent(ftoken)) + return reverse(listSoFar); + const value token = content(ftoken); + if(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) { + const value val = c_str(listToString(readIdentifierHelper(mklist(chr), in))); + if (val == "false") + return value((bool)false); + if (val == "true") + return value((bool)true); + if (val == "nil") + return value(); + return val; +} + +const list<char> readStringHelper(const list<char>& listSoFar, istream& in) { + const char nextChar = readChar(in); + if(nextChar == -1 || nextChar == '"') + return reverse(listSoFar); + if (nextChar == '\\') { + const char escapedChar = readChar(in); + if (escapedChar == -1) + return reverse(listSoFar); + return readStringHelper(cons(escapedChar, listSoFar), in); + } + return readStringHelper(cons(nextChar, listSoFar), in); +} + +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 failable<value> fnextToken = readToken(in); + if (!hasContent(fnextToken)) + return value(); + const value nextToken = content(fnextToken); + if(isLeftParenthesis(nextToken)) + return readList(list<value>(), in); + return nextToken; +} + +const value readValue(const string s) { + istringstream in(s); + const failable<value> fnextToken = readToken(in); + if (!hasContent(fnextToken)) + return value(); + const value nextToken = content(fnextToken); + 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/lightweight-sca/modules/scheme/json-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp new file mode 100644 index 0000000000..4bdf8bd37f --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Convert a JSON document to a scheme value. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "../json/json.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int jsonValue() { + const js::JSContext cx; + const failable<list<value> > lv = json::readJSON(streamList(cin), cx); + if (!hasContent(lv)) { + cerr << reason(lv) << " : " << rcode(lv); + return 1; + } + cout << writeValue(content(lv)); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::jsonValue(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp new file mode 100644 index 0000000000..59aee12073 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp @@ -0,0 +1,288 @@ +/* + * 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 "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "parallel.hpp" + +namespace tuscany { +namespace scheme { + +const value primitiveSymbol("primitive"); +const value quoteSymbol("'"); +const value lambdaSymbol("lambda"); + +#ifdef WANT_THREADS +perthread_ptr<ostream> displayOutStream; +#else +ostream* displayOutStream = NULL; +#endif + +#ifdef WANT_THREADS +perthread_ptr<ostream> logOutStream; +#else +ostream* logOutStream = NULL; +#endif + +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) { + return mkuuid(); +} + +const value cadrProc(const list<value>& args) { + return cadr((list<value> )car(args)); +} + +const value caddrProc(const list<value>& args) { + return caddr((list<value> )car(args)); +} + +const value cadddrProc(const list<value>& args) { + return cadddr((list<value> )car(args)); +} + +const value cddrProc(const list<value>& args) { + return cddr((list<value> )car(args)); +} + +const value cdddrProc(const list<value>& args) { + return cdddr((list<value> )car(args)); +} + +const value appendProc(const list<value>& args) { + return append((list<value> )car(args), (list<value>)cadr(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" + + "append" + + "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(appendProc) + + 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/lightweight-sca/modules/scheme/scheme-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-shell.cpp new file mode 100644 index 0000000000..4aa67c2375 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-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/lightweight-sca/modules/scheme/scheme-test.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp new file mode 100644 index 0000000000..5b69b8e588 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp @@ -0,0 +1,242 @@ +/* + * 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); + } + { + const list<value> i = mklist<value>("x", value()); + const list<value> a = mklist<value>(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::gc_scoped_pool p; + 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/lightweight-sca/modules/scheme/value-element.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp new file mode 100644 index 0000000000..a4acdaf2d7 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Convert a scheme value to a value representing an element. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int valueElement() { + const value v = valuesToElements(readValue(cin)); + cout << writeValue(v); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::valueElement(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp new file mode 100644 index 0000000000..a8c875fcc8 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Convert a scheme value to a JSON document. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "../json/json.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int valueJSON() { + const js::JSContext cx; + failable<list<string> > s = json::writeJSON(readValue(cin), cx); + if (!hasContent(s)) { + cerr << reason(s) << " : " << rcode(s); + return 1; + } + write(content(s), cout); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::valueJSON(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp new file mode 100644 index 0000000000..ff785899c6 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Convert a scheme value to an XML document. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "xml.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int valueXML() { + failable<list<string> > s = writeXML(readValue(cin)); + if (!hasContent(s)) { + cerr << reason(s) << " : " << rcode(s); + return 1; + } + write(content(s), cout); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::valueXML(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp new file mode 100644 index 0000000000..d88f754aa5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp @@ -0,0 +1,47 @@ +/* + * 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$ */ + +/** + * Convert an XML document to a scheme value. + */ + +#include "fstream.hpp" +#include "string.hpp" +#include "xml.hpp" +#include "element.hpp" +#include "eval.hpp" + +namespace tuscany { +namespace scheme { + +int xmlValue() { + const value v = readXML(streamList(cin)); + cout << writeValue(v); + return 0; +} + +} +} + +int main() { + return tuscany::scheme::xmlValue(); +} + diff --git a/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am new file mode 100644 index 0000000000..e2fd67d9b8 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am @@ -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. + +INCLUDES = -I${HTTPD_INCLUDE} + +incl_HEADERS = *.hpp +incldir = $(prefix)/include/modules/server + +dist_mod_SCRIPTS = cpp-conf scheme-conf server-conf +moddir = $(prefix)/modules/server + +EXTRA_DIST = domain-test.composite client-test.scm server-test.scm htdocs/*.html htdocs/test/*.xml htdocs/test/*.txt + +mod_LTLIBRARIES = libmod_tuscany_eval.la +noinst_DATA = libmod_tuscany_eval${libsuffix} + +libmod_tuscany_eval_la_SOURCES = mod-eval.cpp +libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_eval${libsuffix}: + ln -s .libs/libmod_tuscany_eval${libsuffix} + +noinst_test_LTLIBRARIES = libimpl-test.la +noinst_testdir = `pwd`/tmp +noinst_DATA += libimpl-test${libsuffix} + +libimpl_test_la_SOURCES = impl-test.cpp +libimpl-test${libsuffix}: + ln -s .libs/libimpl-test${libsuffix} + +client_test_SOURCES = client-test.cpp +client_test_LDFLAGS = -lxml2 -lcurl -lmozjs + +dist_noinst_SCRIPTS = httpd-test server-test wiring-test +noinst_PROGRAMS = client-test +TESTS = httpd-test server-test wiring-test + diff --git a/sca-cpp/branches/lightweight-sca/modules/server/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/server/client-test.cpp new file mode 100644 index 0000000000..3f5ff20c56 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/scheme"; + + tuscany::server::testServer(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp b/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp new file mode 100644 index 0000000000..1c7b26da39 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp @@ -0,0 +1,372 @@ +/* + * 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/http.hpp" + +namespace tuscany { +namespace server { + +string testURI = "http://localhost:8090/scheme"; +bool testBlobs = true; + +ostream* curlWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +const bool testGet() { + gc_scoped_pool pool; + http::CURLSession ch("", "", "", "", 0); + { + 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("", "", "", "", 0); + 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("", "", "", "", 0); + const failable<value> r = http::evalExpr(mklist<value>(string("echo"), string("Hello")), testURI, ch); + assert(hasContent(r)); + assert(content(r) == 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 failable<value> r = http::evalExpr(mklist<value>(string("echo"), string("Hello")), uri, ch); + assert(hasContent(r)); + assert(content(r) == 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 failable<value> r = content(http::evalExpr(mklist<value>(string("echo"), blobs), uri, ch)); + assert(hasContent(r)); + assert(content(r) == blobs); + return true; + } +}; + +const bool testEvalPerf() { + gc_scoped_pool pool; + http::CURLSession ch("", "", "", "", 0); + 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>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99"))); + const list<value> a = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b")) + + i); + http::CURLSession ch("", "", "", "", 0); + 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("", "", "", "", 0); + { + const list<value> i = list<value>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99"))); + const list<value> val = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + 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>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "blob1" + blob) + + (list<value>() + "blob2" + blob) + + (list<value>() + "price" + string("$2.99"))); + const list<value> val = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + 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("", "", "", "", 0); + 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; + worker& w; + const int threads; + postThreadLoop(const lambda<bool()>& l, worker& w, const int threads) : l(l), w(w), threads(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; + worker w(threads); + + const list<value> i = list<value>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99"))); + const value val = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + 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, w, 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("", "", "", "", 0); + 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>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99"))); + const value val = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + 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>() + "content" + (list<value>() + "item" + + (list<value>() + "name" + string("Apple")) + + (list<value>() + "price" + string("$2.99"))); + const list<value> a = list<value>() + (list<value>() + "entry" + + (list<value>() + "title" + string("item")) + + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b")) + + i); + http::CURLSession ch("", "", "", "", 0); + 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("", "", "", "", 0); + 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/lightweight-sca/modules/server/client-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/client-test.scm new file mode 100644 index 0000000000..47b799d390 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/server/cpp-conf b/sca-cpp/branches/lightweight-sca/modules/server/cpp-conf new file mode 100755 index 0000000000..6b74f60ec5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/cpp-conf @@ -0,0 +1,37 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +cat >>$root/conf/modules.conf <<EOF +# Generated by: cpp-conf $* +# Support for C++ SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval$libsuffix + +EOF diff --git a/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite new file mode 100644 index 0000000000..1819b3bf4c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite @@ -0,0 +1,58 @@ +<?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" + targetNamespace="http://domain/test" + name="domain-test"> + + <component name="scheme-test"> + <implementation.scheme script="server-test.scm"/> + <service name="scheme"> + <binding.http uri="scheme"/> + </service> + </component> + + <component name="property-test"> + <implementation.scheme script="property-test.scm"/> + <service name="properties"> + <binding.http uri="properties"/> + </service> + <property name="host"></property> + <property name="path"></property> + <property name="query"></property> + </component> + + <component name="cpp-test"> + <implementation.cpp path="." library="libimpl-test"/> + <service name="cpp"> + <binding.http uri="cpp"/> + </service> + </component> + + <component name="client-test"> + <implementation.scheme script="client-test.scm"/> + <service name="client"> + <binding.http uri="client"/> + </service> + <reference name="ref" target="scheme-test"> + <binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html new file mode 100644 index 0000000000..236864edfb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html @@ -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. +--> + +<html> +<head> +<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/> +<meta name="apple-mobile-web-app-capable" content="yes"/> +<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> +<link rel="stylesheet" type="text/css" href="/ui-min.css"/> +<title>It works</title> +</head> +<body> +<h1>It works!</h1> +</body> +</html> + diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml new file mode 100644 index 0000000000..46053c3138 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml @@ -0,0 +1,14 @@ +<?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> + <name>Apple</name> + <currencyCode>USD</currencyCode> + <currencySymbol>$</currencySymbol> + <price>2.99</price> + </item> + </content> + <link href="111"/> +</entry> diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml new file mode 100644 index 0000000000..337320e4c5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml @@ -0,0 +1,44 @@ +<?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> + <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> + <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</name> + <currencyCode>USD</currencyCode> + <currencySymbol>$</currencySymbol> + <price>1.55</price> + </item> + </content> + <link href="333"/> + </entry> +</feed> diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt new file mode 100644 index 0000000000..2cff8b7339 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt @@ -0,0 +1,14 @@ +{ + "id": "1", + "result": { + "host": "localhost", + "path": [ + "c", + "property-test" + ], + "query": { + "id": "1", + "method": "print" + } + } +}
\ No newline at end of file diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt new file mode 100644 index 0000000000..b5c2457309 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt @@ -0,0 +1,7 @@ +{ + "id": 1, + "method": "echo", + "params": [ + "Hello" + ] +} diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt new file mode 100644 index 0000000000..72b27b67db --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt @@ -0,0 +1,4 @@ +{ + "id": 1, + "result": "Hello" +}
\ No newline at end of file diff --git a/sca-cpp/branches/lightweight-sca/modules/server/httpd-test b/sca-cpp/branches/lightweight-sca/modules/server/httpd-test new file mode 100755 index 0000000000..195caa4562 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/httpd-test @@ -0,0 +1,87 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +rm -rf tmp +../http/httpd-conf tmp localhost 8090 htdocs +../http/httpd-event-conf tmp +./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/scheme/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/test/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/scheme/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/test/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/scheme/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/scheme/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/scheme/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl http://localhost:8090/scheme/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/test/json-result.txt + rc=$? +fi + +# Test built-in properties +if [ "$rc" = "0" ]; then + $curl_prefix/bin/curl 'http://localhost:8090/properties?id=1&method=print' >tmp/json-properties.txt 2>/dev/null + diff tmp/json-properties.txt htdocs/test/json-properties.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp new file mode 100644 index 0000000000..1bd0843745 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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/lightweight-sca/modules/server/mod-cpp.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-cpp.hpp new file mode 100644 index 0000000000..8cae35e493 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-cpp.hpp @@ -0,0 +1,104 @@ +/* + * 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/lightweight-sca/modules/server/mod-eval.cpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp new file mode 100644 index 0000000000..3fd69c1fea --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#define WANT_HTTPD_LOG 1 +#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); + if (contains(itype, ".widget")) + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype, -1, false); + return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype); +} + +} +} +} diff --git a/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp new file mode 100644 index 0000000000..ee99baa039 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp @@ -0,0 +1,1600 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more provider 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 <sys/stat.h> + +#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 "../scheme/io.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../scdl/scdl.hpp" +#include "../http/http.hpp" +#include "../http/httpd.hpp" + +#include "apr_md5.h" +#include "ap_provider.h" +#include "mod_auth.h" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * SSL certificate configuration. + */ +class SSLConf { +public: + SSLConf() { + } + + string ca; + string cert; + string key; +}; + +/** + * Virtual host configuration. + */ +class VhostConf { +public: + VhostConf() { + } + + string domain; + string contribPath; + string composName; + string contributorName; + value contributor; + string authenticatorName; + value authenticator; +}; + +/** + * Contribution configuration. + */ +class ContribConf { +public: + ContribConf() { + } + + string contribPath; + string composName; +}; + +/** + * Composite assocs. + */ +class Composite { +public: + Composite() { + } + + Composite(const list<value>& refs, const list<value>& svcs, const list<value>& impls) : refs(refs), svcs(svcs), impls(impls) { + } + + list<value> refs; + list<value> svcs; + list<value> impls; +}; + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf() { + } + + ServerConf(apr_pool_t* p, const server_rec* s) : p(p), server(s), timeout(0) { + } + + const gc_pool p; + const server_rec* server; + lambda<value(const list<value>&)> lifecycle; + ContribConf contribc; + SSLConf sslc; + int timeout; + VhostConf vhostc; + Composite compos; +}; + +/** + * Request configuration. + */ +class RequestConf { +public: + RequestConf(apr_pool_t* p, const request_rec* r) : p(p), request(r), vhost(false), valias(false) { + } + + const gc_pool p; + const request_rec* request; + bool vhost; + bool valias; + list<value> rpath; + list<value> vpath; + list<value> impls; +}; + +/** + * Authentication cache store function. + */ +static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authnCacheStore = NULL; + +/** + * Convert a result represented as a (content reason? code?) tuple 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)), isNil(cddr(v))? -1 : (int)caddr(v), false); +} + +/** + * Store current HTTP request for access from property and proxy lambda functions. + */ +#ifdef WANT_THREADS +perthread_ptr<request_rec> currentRequest; +#else +request_rec* currentRequest = NULL; +#endif + +class ScopedRequest { +public: + ScopedRequest(request_rec* r) { + currentRequest = r; + } + + ~ScopedRequest() { + currentRequest = NULL; + } +}; + +/** + * Make an HTTP proxy lambda to an absolute URI + */ +const value mkhttpProxy(const string& uri, const int timeout, const gc_pool& p) { + debug(uri, "modeval::mkhttpProxy::uri"); + return lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p)); +} + +/** + * Return a component implementation proxy lambda. + */ +class implProxy { +public: + implProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) : name(name), impls(impls), sslc(sslc), timeout(timeout) { + } + + const value callImpl(const value& cname, const list<value>& aparams) const { + //debug(impls, "modeval::implProxy::callImpl::impls"); + + // Lookup the component implementation + const list<value> impl(assoctree<value>(cname, impls)); + if (isNil(impl)) + return mkfailure<value>(string("Couldn't find component implementation: ") + cname); + + // Call its lambda function + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + const value func = c_str(car(aparams)); + const failable<value> val = failableResult(l(cons(func, cdr(aparams)))); + debug(val, "modeval::implProxy::result"); + if (!hasContent(val)) + return value(); + return content(val); + } + + const value operator()(const list<value>& params) const { + debug(name, "modeval::implProxy::name"); + debug(params, "modeval::implProxy::input"); + + // If the reference was 'wiredByImpl' use the first param as target + if (isNil(name)) { + const value uri = cadr(params); + const list<value> aparams = cons<value>(car(params), cddr(params)); + debug(uri, "modeval::implProxy::wiredByImpl::uri"); + debug(aparams, "modeval::implProxy::wiredByImpl::input"); + + // Use an HTTP proxy if the target is an absolute :// target + if (http::isAbsolute(uri)) { + gc_pool p(currentRequest->pool); + + // Interpret a uri in the form app://appname, convert it using the scheme, + // top level domain and port number from the current request + if (http::scheme(uri, p) == "app") { + ostringstream appuri; + appuri << httpd::scheme(currentRequest) << "://" << substr(uri, 6) << "." << http::topDomain(httpd::hostName(currentRequest)) << ":" << httpd::port(currentRequest) << "/"; + debug(str(appuri), "modeval::implProxy::httpproxy::appuri"); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(str(appuri), sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p)); + return px(aparams); + } + + // Pass our SSL certificate and the cookie from the current request + // only if the target is in the same top level domain + if (http::topDomain(http::hostName(uri, p)) == http::topDomain(httpd::hostName(currentRequest))) { + debug(uri, "modeval::implProxy::httpproxy::samedomain"); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p)); + return px(aparams); + } + + // No SSL certificate or cookie on a cross domain call + debug(uri, "modeval::implProxy::httpproxy::crossdomain"); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p)); + return px(aparams); + } + + // Call the component implementation + return callImpl(uri, aparams); + } + + // Call the component implementation + return callImpl(name, params); + } + +private: + const value name; + const list<value>& impls; + const SSLConf& sslc; + const int timeout; +}; + +const value mkimplProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) { + debug(name, "modeval::implProxy::impl"); + return lambda<value(const list<value>&)>(implProxy(name, impls, sslc, timeout)); +} + +/** + * Return a proxy lambda for an unwired reference. + */ +class unwiredProxy { +public: + unwiredProxy(const value& name) : name(name) { + } + + const value operator()(const list<value>& params) const { + debug(name, "modeval::unwiredProxy::name"); + debug(params, "modeval::unwiredProxy::input"); + + // Get function returns a default empty value + if (car(params) == "get") { + debug(value(), "modeval::unwiredProxy::result"); + return value(); + } + + // All other functions return a failure + return mkfailure<value>(string("Reference is not wired: ") + name); + } + +private: + const value name; +}; + +/** + * Make a proxy lambda for an unwired reference. + */ +const value mkunwiredProxy(const string& ref) { + debug(ref, "modeval::mkunwiredProxy::ref"); + return lambda<value(const list<value>&)>(unwiredProxy(ref)); +} + +/** + * Convert a list of component references to a list of proxy lambdas. + */ +const value mkrefProxy(const value& ref, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) { + const value target = scdl::target(ref); + const bool wbyimpl = scdl::wiredByImpl(ref); + debug(ref, "modeval::mkrefProxy::ref"); + debug(target, "modeval::mkrefProxy::target"); + debug(wbyimpl, "modeval::mkrefProxy::wiredByImpl"); + + // Use an HTTP proxy or an internal proxy to the component implementation + if (wbyimpl) + return mkimplProxy(value(), impls, sslc, timeout); + if (isNil(target)) + return mkunwiredProxy(scdl::name(ref)); + if (http::isAbsolute(target)) + return mkhttpProxy(target, timeout, p); + return mkimplProxy(car(pathValues(target)), impls, sslc, timeout); +} + +const list<value> refProxies(const list<value>& refs, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) { + if (isNil(refs)) + return refs; + return cons(mkrefProxy(car(refs), impls, sslc, timeout, p), refProxies(cdr(refs), impls, sslc, timeout, p)); +} + +/** + * Convert a list of component properties to a list of lambda functions that just return + * the property value. The host, user and email properties are configured with the values + * from the HTTP request, if any. + */ +struct propProxy { + const value v; + propProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + return v; + } +}; + +struct hostPropProxy { + const value v; + hostPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return http::hostName(); + const value h = httpd::hostName(currentRequest, v); + debug(h, "modeval::hostPropProxy::value"); + return h; + } +}; + +struct appPropProxy { + const value v; + appPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval); + const value a = isNil(reqc.vpath)? v : car(reqc.vpath); + debug(a, "modeval::appPropProxy::value"); + return a; + } +}; + +struct pathPropProxy { + const value v; + pathPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval); + const value p = reqc.rpath; + debug(p, "modeval::pathPropProxy::value"); + return p; + } +}; + +struct queryPropProxy { + const value v; + queryPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const value q = httpd::unescapeArgs(httpd::queryArgs(currentRequest)); + debug(q, "modeval::queryPropProxy::value"); + return q; + } +}; + +struct envPropProxy { + const string name; + const value v; + envPropProxy(const string& name, const value& v) : name(name), v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const char* env = apr_table_get(currentRequest->subprocess_env, c_str(name)); + if (env == NULL || *env == '\0') + return v; + debug(name, "modeval::envPropProxy::name"); + const value e = string(env); + debug(e, "modeval::envPropProxy::value"); + return e; + } +}; + +struct realmPropProxy { + const value v; + realmPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const char* env = apr_table_get(currentRequest->subprocess_env, "REALM"); + if (env == NULL) + return v; + const string realm = httpd::realm(string(env)); + if (length(realm) == 0) + return v; + const value r = realm; + debug(r, "modeval::realmPropProxy::value"); + return r; + } +}; + +struct timeoutPropProxy { + const value v; + timeoutPropProxy(const value& v) : v(atoi(c_str((string)v))) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const ServerConf& sc = httpd::serverConf<ServerConf>(currentRequest, &mod_tuscany_eval); + const value r = sc.timeout; + debug(r, "modeval::timeoutPropProxy::value"); + return r; + } +}; + +struct userPropProxy { + const value v; + userPropProxy(const value& v) : v(v) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + if (currentRequest->user == NULL) + return v; + const value u = string(currentRequest->user); + debug(u, "modeval::userPropProxy::value"); + return u; + } +}; + +const value mkpropProxy(const value& prop) { + const value n = scdl::name(prop); + const value v = elementHasValue(prop)? elementValue(prop):value(string("")); + if (n == "app") + return lambda<value(const list<value>&)>(appPropProxy(v)); + if (n == "host") + return lambda<value(const list<value>&)>(hostPropProxy(v)); + if (n == "path") + return lambda<value(const list<value>&)>(pathPropProxy(v)); + if (n == "query") + return lambda<value(const list<value>&)>(queryPropProxy(v)); + if (n == "user") + return lambda<value(const list<value>&)>(userPropProxy(v)); + if (n == "realm") + return lambda<value(const list<value>&)>(realmPropProxy(v)); + if (n == "timeout") + return lambda<value(const list<value>&)>(timeoutPropProxy(v)); + if (n == "email") + return lambda<value(const list<value>&)>(envPropProxy("EMAIL", v)); + if (n == "nickname") + return lambda<value(const list<value>&)>(envPropProxy("NICKNAME", v)); + if (n == "fullname") + return lambda<value(const list<value>&)>(envPropProxy("FULLNAME", v)); + if (n == "firstname") + return lambda<value(const list<value>&)>(envPropProxy("FIRSTNAME", v)); + if (n == "lastname") + return lambda<value(const list<value>&)>(envPropProxy("LASTNAME", v)); + return lambda<value(const list<value>&)>(propProxy(v)); +} + +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. + */ +struct implementationFailure { + const value reason; + implementationFailure(const value& r) : reason(r) { + } + + // Default implementation representing an implementation that + // couldn't be evaluated. Report the evaluation error on all + // subsequent calls expect start/stop. + const value operator()(unused const list<value>& params) const { + const value func = car(params); + if (func == "start" || func == "stop") + return mklist<value>(lambda<value(const list<value>&)>()); + return mklist<value>(value(), reason); + } +}; + +const value evalComponent(const string& contribPath, const value& comp, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { + 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); + debug(comp, "modeval::evalComponent::comp"); + debug(impl, "modeval::evalComponent::impl"); + + // Convert component references to configured proxy lambdas + const list<value> rpx(refProxies(scdl::references(comp), impls, sslc, timeout, p)); + + // Convert component properties 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(contribPath, impl, append(rpx, ppx), lifecycle)); + if (!hasContent(cimpl)) + return lambda<value(const list<value>&)>(implementationFailure(reason(cimpl))); + return content(cimpl); +} + +/** + * Return a list of component-name + configured-implementation pairs. + */ +const list<value> componentToImplementationAssoc(const list<value>& c, const string& contribPath, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { + if (isNil(c)) + return c; + return cons<value>(mklist<value>(scdl::name(car(c)), + evalComponent(contribPath, car(c), impls, lifecycle, sslc, timeout, p)), + componentToImplementationAssoc(cdr(c), contribPath, impls, lifecycle, sslc, timeout, p)); +} + +/** + * 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))); +} + +/** + * Get the components returned by a contributor. + */ +const failable<list<value> > getComponents(const lambda<value(const list<value>&)>& contributor, const string& name) { + const failable<value> val = failableResult(contributor(cons<value>("get", mklist<value>(mklist<value>(name))))); + if (!hasContent(val)) + return mkfailure<list<value> >(val); + const list<value> c = assoc<value>(value("composite"), assoc<value>(value("content"), (list<list<value> >)cdr<value>(car<value>(content(val))))); + debug(c, "modeval::getComponents::comp"); + if (isNil(c)) + return mkfailure<list<value> >(string("Could not get composite: ") + name); + const failable<list<string> > x = writeXML(car<value>(valuesToElements(mklist<value>(mklist<value>(c))))); + if (!hasContent(x)) + return mkfailure<list<value> >(x); + return scdl::components(readXML(content(x))); +} + +/** + * 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> >(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)); +} + +/** + * 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 deployed composite. + */ +const failable<Composite> confComponents(const string& contribPath, const string& composName, const value& contributor, const string& vhost, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { + debug(contribPath, "modeval::confComponents::contribPath"); + debug(composName, "modeval::confComponents::composName"); + debug(contributor, "modeval::confComponents::contributor"); + debug(vhost, "modeval::confComponents::vhost"); + debug(impls, "modeval::confComponents::impls"); + + const failable<list<value> > fcomps = isNil(contributor)? + readComponents(scdl::resourcePath(length(vhost) != 0? contribPath + vhost + "/" : contribPath, composName)) : + getComponents(contributor, vhost); + if (!hasContent(fcomps)) + return mkfailure<Composite>(fcomps); + + const list<value> comps = content(fcomps); + debug(comps, "modeval::confComponents::comps"); + + const list<value> refs = mkbtree(sort(componentReferenceToTargetAssoc(comps))); + debug(flatten(refs), "modeval::confComponents::refs"); + + const list<value> svcs = mkbtree(sort(uriToComponentAssoc(comps))); + debug(flatten(svcs), "modeval::confComponents::svcs"); + + const list<value> cimpls = mkbtree(sort(componentToImplementationAssoc(comps, + isNil(contributor)? length(vhost) != 0? contribPath + vhost + "/" : contribPath : contribPath, + impls, lifecycle, sslc, timeout, p))); + debug(flatten(cimpls), "modeval::confComponents::impls"); + + return Composite(refs, svcs, cimpls); +} + +/** + * Start the components declared in a composite. + */ +const failable<list<value> > startComponents(const list<value>& impls) { + debug(flatten(impls), "modeval::startComponents::impls"); + const failable<list<value> > fsimpls = applyLifecycleExpr(flatten(impls), mklist<value>("start")); + if (!hasContent(fsimpls)) + return mkfailure<list<value> >(fsimpls); + + const list<value> simpls = content(fsimpls); + debug(impls, "modeval::startComponents::simpls"); + return mkbtree(sort(simpls)); +} + +/** + * Stop the components declared in a composite. + */ +const failable<bool> stopComponents(const list<value>& simpls) { + debug(flatten(simpls), "modeval::stopComponents::simpls"); + applyLifecycleExpr(flatten(simpls), mklist<value>("stop")); + return true; +} + +/** + * Handle an HTTP GET. + */ +const failable<int> get(const list<value>& rpath, 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, json::queryParams(args)))); + if (!hasContent(val)) + return mkfailure<int>(val); + + // Return JSON result + js::JSContext cx; + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r); + } + + // Evaluate the GET expression + const list<value> params(cddr(rpath)); + const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(params)))); + if (!hasContent(val)) + return mkfailure<int>(val); + const value c = content(val); + debug(c, "modeval::get::content"); + + // Return a nil value as a not found status + if (!isList(c) && isNil(c)) + return HTTP_NOT_FOUND; + + // Check if the client requested a specific format + const list<value> fmt = assoc<value>("format", args); + + // Write as a scheme value if requested by the client + if (!isNil(fmt) && cadr(fmt) == "scheme") + return httpd::writeResult(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8", r); + + // Write a simple value as a JSON value + if (!isList(c)) { + js::JSContext cx; + if (isSymbol(c)) { + const list<value> lc = mklist<value>(mklist<value>("name", value(string(c)))); + debug(lc, "modeval::get::symbol"); + return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r); + } + + const list<value> lc = mklist<value>(mklist<value>("value", c)); + debug(lc, "modeval::get::value"); + return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r); + } + + // Write an empty list as a JSON empty value + if (isNil((list<value>)c)) { + js::JSContext cx; + debug(list<value>(), "modeval::get::empty"); + return httpd::writeResult(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8", r); + } + + // Write content-type / content-list pair + if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c))) + return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r); + + // Write an assoc value as a JSON result + if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) { + js::JSContext cx; + const list<value> lc = mklist<value>(c); + debug(lc, "modeval::get::assoc"); + debug(valuesToElements(lc), "modeval::get::assoc::element"); + return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r); + } + + // Write value as JSON if requested by the client + if (!isNil(fmt) && cadr(fmt) == "json") { + js::JSContext cx; + return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8", r); + } + + // Convert list of values to element values + const list<value> e = valuesToElements(c); + debug(e, "modeval::get::elements"); + + // Write an ATOM feed or entry + if (isList(car<value>(e)) && !isNil(car<value>(e))) { + const list<value> el = car<value>(e); + if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) { + if (cadr<value>(el) == atom::feed) + return httpd::writeResult(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8", r); + if (cadr<value>(el) == atom::entry) + return httpd::writeResult(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8", r); + } + } + + // Write any other compound value as a JSON value + js::JSContext cx; + return httpd::writeResult(json::writeJSON(e, cx), "application/json; charset=utf-8", r); +} + +/** + * Handle an HTTP POST. + */ +const failable<int> post(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::post::uri"); + + // 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") || contains(ct, "application/x-www-form-urlencoded")) { + + // 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"); + js::JSContext 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>(val); + + // Return JSON result + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", 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 int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + const list<string> ls = httpd::read(r); + debug(ls, "modeval::post::input"); + const value aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls))); + + // Evaluate the POST expression + const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(rpath), aval)))); + if (!hasContent(val)) + return mkfailure<int>(val); + + // Return the created resource location + debug(content(val), "modeval::post::location"); + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(httpd::url(r->uri, 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>(val); + return (int)content(val); +} + +/** + * Handle an HTTP PUT. + */ +const failable<int> put(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::put::uri"); + + // Read the ATOM entry + 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 aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls))); + + // Evaluate the PUT expression and update the corresponding resource + const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(rpath), aval)))); + if (!hasContent(val)) + return mkfailure<int>(val); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable<int> del(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) { + debug(r->uri, "modeval::delete::uri"); + + // Evaluate an ATOM delete request + const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(rpath))))); + if (!hasContent(val)) + return mkfailure<int>(val); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Proceed to handle a service component request. + */ +int proceedToHandler(request_rec* r, const int rc) { + r->handler = "mod_tuscany_eval"; + return rc; +} + +int proceedToHandler(request_rec* r, const int rc, const bool valias, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) { + r->handler = "mod_tuscany_eval"; + r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:") + r->uri)); + + // Store the selected vhost, path and composite in the request + RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval); + reqc.valias = valias; + reqc.rpath = rpath; + reqc.vpath = vpath; + reqc.impls = impls; + return rc; +} + +/** + * Route a component request to the specified component. + */ +int translateComponent(request_rec *r, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) { + debug(rpath, "modeval::translateComponent::rpath"); + debug(flatten(impls), "modeval::translateComponent::impls"); + + // Find the requested component + if (isNil(cdr(rpath))) + return HTTP_NOT_FOUND; + const list<value> impl(assoctree(cadr(rpath), impls)); + if (isNil(impl)) + return HTTP_NOT_FOUND; + debug(impl, "modeval::translateComponent::impl"); + + return proceedToHandler(r, OK, false, rpath, vpath, impls);; +} + +/** + * Route a /references/component-name/reference-name request, + * to the target of the component reference. + */ +int translateReference(request_rec *r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& impls) { + debug(rpath, "modeval::translateReference::rpath"); + debug(flatten(refs), "modeval::translateReference::refs"); + + // Find the requested component + if (isNil(cdr(rpath))) + return HTTP_NOT_FOUND; + const list<value> comp(assoctree(cadr(rpath), refs)); + if (isNil(comp)) + return HTTP_NOT_FOUND; + debug(comp, "modeval::translateReference::comp"); + + // Find the requested reference and target configuration + const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp))); + if (isNil(ref)) + return HTTP_NOT_FOUND; + debug(ref, "modeval::translateReference::ref"); + + const string target(cadr(ref)); + debug(target, "modeval::translateReference::target"); + + // Route to an absolute target URI using mod_proxy or an HTTP client redirect + const list<value> pathInfo = cdddr(rpath); + if (http::isAbsolute(target)) { + string turi = target + path(pathInfo) + (r->args != NULL? string("?") + string(r->args) : string("")); + const string proxy(string("proxy:") + turi); + debug(proxy, "modeval::translateReference::proxy"); + r->filename = apr_pstrdup(r->pool, c_str(proxy)); + r->proxyreq = PROXYREQ_REVERSE; + r->handler = "proxy-server"; + apr_table_setn(r->notes, "proxy-nocanon", "1"); + return OK; + } + + // Route to a relative target URI using a local internal redirect + // / c / target component name / request path info + const value tname = substr(target, 0, find(target, '/')); + const list<value> redir = cons<value>(string("c"), cons(tname, pathInfo)); + debug(redir, "modeval::translateReference::redirect"); + return proceedToHandler(r, OK, false, redir, vpath, impls);; +} + +/** + * 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, const list<value>& rpath, const list<value>& vpath, const list<value>& svcs, const list<value>& impls) { + debug(rpath, "modeval::translateService::rpath"); + debug(flatten(svcs), "modeval::translateService::svcs"); + + // Find the requested component + if (isNil(rpath)) + return HTTP_NOT_FOUND; + const list<value> svc(assocPath(rpath, svcs)); + if (isNil(svc)) + return DECLINED; + debug(svc, "modeval::translateService::svc"); + + // Dispatch to the target component using a local internal redirect + // / c / target component name / request path info + const list<value> redir = cons<value>(string("c"), cons<value>(cadr(svc), httpd::pathInfo(rpath, car(svc)))); + debug(redir, "modeval::translateService::redirect"); + return proceedToHandler(r, OK, false, redir, vpath, impls); +} + +/** + * Translate a request to the target app and component. + */ +const int translateRequest(request_rec* r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& svcs, const list<value>& impls) { + debug(vpath, "modeval::translateRequest::vpath"); + debug(rpath, "modeval::translateRequest::rpath"); + const string prefix = isNil(rpath)? "" : car(rpath); + + // Translate a component request + if ((prefix == string("components") || prefix == string("c")) && translateComponent(r, rpath, vpath, impls) == OK) + return proceedToHandler(r, OK); + + // Translate a component reference request + if ((prefix == string("references") || prefix == string("r")) && translateReference(r, rpath, vpath, refs, impls) == OK) + return proceedToHandler(r, OK); + + // Attempt to translate the request to a service request + if (translateService(r, rpath, vpath, svcs, impls) == OK) + return proceedToHandler(r, OK); + + // Attempt to map a request targeting the main host to an actual file + if (isNil(vpath)) { + const failable<request_rec*> fnr = httpd::internalSubRequest(r->uri, r); + if (!hasContent(fnr)) + return rcode(fnr); + request_rec* nr = content(fnr); + nr->uri = r->filename; + const int tr = ap_core_translate(nr); + if (tr != OK) + return tr; + if (ap_directory_walk(nr) == OK && ap_file_walk(nr) == OK && nr->finfo.filetype != APR_NOFILE) { + + // Found the target file, let the default handler serve it + debug(nr->filename, "modeval::translateRequest::file"); + return DECLINED; + } + } else { + + // Make sure a document root request ends with a '/' using + // an external redirect + if (isNil(rpath) && r->uri[strlen(r->uri) - 1] != '/') { + const string target = string(r->uri) + string("/") + (r->args != NULL? string("?") + string(r->args) : string("")); + debug(target, "modeval::translateRequest::location"); + return proceedToHandler(r, httpd::externalRedirect(target, r)); + } + + // If the request didn't match a service, reference or component, + // redirect it to / v / app / path. This will allow mapping to + // the actual app resource using HTTPD aliases. + debug(true, "modeval::translateRequest::valias"); + return proceedToHandler(r, OK, true, rpath, vpath, impls); + } + + return HTTP_NOT_FOUND; +} + +/** + * Translate a request. + */ +int translate(request_rec *r) { + if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE) + return DECLINED; + + gc_scoped_pool pool(r->pool); + + debug_httpdRequest(r, "modeval::translate::input"); + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + + // Parse the request path + const list<value> rpath = pathValues(r->uri); + + // Let default handler handle a resource request + const string prefix = isNil(rpath)? "" : car(rpath); + if (prefix == string("vhosts") || prefix == string("v")) + return DECLINED; + + // Get the request configuration + RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval); + + // If the request is targeting a virtual host, configure the components + // in that virtual host + if (length(sc.vhostc.domain) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor)) && httpd::isVhostRequest(sc.server, sc.vhostc.domain, r)) { + const string vname = http::subDomain(httpd::hostName(r)); + const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); + if (!hasContent(fvcompos)) + return DECLINED; + const Composite vcompos = content(fvcompos); + + // Flag the request as virtual host based + reqc.vhost = true; + + // Translate the request + reqc.impls = vcompos.impls; + return translateRequest(r, rpath, mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls); + } + + // Translate a request targeting the main host + const int rc = translateRequest(r, rpath, list<value>(), sc.compos.refs, sc.compos.svcs, sc.compos.impls); + if (rc != HTTP_NOT_FOUND) + return rc; + + // Attempt to map the first segment of the request path to a virtual host + if (length(prefix) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor))) { + const string vname = prefix; + const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); + if (!hasContent(fvcompos)) + return DECLINED; + const Composite vcompos = content(fvcompos); + + // Translate the request + reqc.impls = vcompos.impls; + return translateRequest(r, cdr(rpath), mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls); + } + return DECLINED; +} + +/** + * Handle a component request. + */ +const int handleRequest(const list<value>& rpath, request_rec *r, const list<value>& impls) { + debug(rpath, "modeval::handleRequest::path"); + + // Get the component implementation lambda + const list<value> impl(assoctree<value>(cadr(rpath), impls)); + if (isNil(impl)) { + mkfailure<int>(string("Couldn't find component implementation: ") + cadr(rpath)); + return HTTP_NOT_FOUND; + } + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + + // Handle HTTP method + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return httpd::reportStatus(get(rpath, r, l)); + if(r->method_number == M_POST) + return httpd::reportStatus(post(rpath, r, l)); + if(r->method_number == M_PUT) + return httpd::reportStatus(put(rpath, r, l)); + if(r->method_number == M_DELETE) + return httpd::reportStatus(del(rpath, r, l)); + return HTTP_NOT_IMPLEMENTED; +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + if (r->handler != NULL && r->handler[0] != '\0') + return DECLINED; + + // Attempt to translate the request + const int trc = translate(r); + + // Pass if we couldn't translate the request + if(trc != OK) + return trc; + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + + // Create a scope for the current request + gc_scoped_pool pool(r->pool); + ScopedRequest sr(r); + + debug_httpdRequest(r, "modeval::handler::input"); + + // Get the request configuration + RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval); + + // Handle an internal redirect as directed by the translate hook + if (reqc.valias) { + const string redir = path(cons<value>(string("v"), reqc.vhost? reqc.vpath : list<value>())) + string(r->uri) + (r->args != NULL? string("?") + string(r->args) : string("")); + debug(redir, "modeval::handler::internalredirect"); + return httpd::internalRedirect(redir, r); + } + if (isNil(reqc.rpath)) + return HTTP_NOT_FOUND; + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + + // Handle a request targeting a component in a virtual host + if (!isNil(reqc.vpath)) { + + // Start the components in the virtual host + const failable<list<value> > fsimpls = startComponents(reqc.impls); + if (!hasContent(fsimpls)) + return HTTP_INTERNAL_SERVER_ERROR; + const list<value> simpls = content(fsimpls); + + // Merge the components in the virtual host with the components in the main host + reqc.impls = mkbtree(sort(append(flatten(sc.compos.impls), flatten(simpls)))); + + // Handle the request against the running components + const int rc = handleRequest(reqc.rpath, r, reqc.impls); + + // Stop the components in the virtual host + stopComponents(simpls); + return rc; + } + + // Handle a request targeting a component in the main host + return handleRequest(reqc.rpath, r, sc.compos.impls); +} + +/** + * Call an authenticator component to check a user's password. + */ +authn_status checkPassword(request_rec* r, const char* u, const char* p) { + gc_scoped_pool pool(r->pool); + + // Prevent FakeBasicAuth spoofing + const string user = u; + const string password = p; + debug(user, "modeval::checkPassword::user"); + if (substr(user, 0, 1) != "/" && find(user, "/") != length(user) && password == "password") { + mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + return AUTH_DENIED; + } + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + if (isNil(sc.vhostc.authenticator)) { + mkfailure<int>("SCA authenticator not configured"); + return AUTH_GENERAL_ERROR; + } + + // Retrieve the user's password hash + const list<value> uid = pathValues(user); + const failable<value> val = failableResult(sc.vhostc.authenticator(cons<value>("get", mklist<value>(uid)))); + if (!hasContent(val)) { + mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user); + return AUTH_USER_NOT_FOUND; + } + const value hval = content(val); + const list<value> hcontent = isList(hval) && !isNil(hval) && isList(car<value>(hval)) && !isNil(car<value>(hval))? assoc<value>(value("content"), cdr<value>(car<value>(hval))) : list<value>(); + const list<value> hassoc = isNil(hcontent)? list<value>() : assoc<value>(value("hash"), cdr<value>(hcontent)); + if (isNil(hassoc)) { + mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user); + return AUTH_USER_NOT_FOUND; + } + const string hash = cadr<value>(hassoc); + if (length(hash) == 0) { + mkfailure<int>(string("SCA authentication check user failed: ") + user); + return AUTH_USER_NOT_FOUND; + } + + // Cache the hash in the auth cache provider, if available + if (authnCacheStore != NULL) + authnCacheStore(r, "component", u, NULL, c_str(hash)); + + // Validate the presented password against the hash + const apr_status_t rv = apr_password_validate(p, c_str(hash)); + if (rv != APR_SUCCESS) { + mkfailure<int>(string("SCA authentication user password check failed: ") + user); + return AUTH_DENIED; + } + return AUTH_GRANTED; +} + +/** + * 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 + stopComponents(sc.compos.impls); + + // 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); + debug(httpd::serverName(s), "modeval::postConfigMerge::serverName"); + sc.lifecycle = mainsc.lifecycle; + sc.contribc = mainsc.contribc; + sc.vhostc = mainsc.vhostc; + if (sc.sslc.ca == "") sc.sslc.ca = mainsc.sslc.ca; + if (sc.sslc.cert == "") sc.sslc.cert = mainsc.sslc.cert; + if (sc.sslc.key == "") sc.sslc.key = mainsc.sslc.key; + sc.timeout = mainsc.timeout; + sc.compos = mainsc.compos; + 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); + + // Get the server configuration and determine the server name + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + debug(httpd::serverName(s), "modeval::postConfig::serverName"); + debug(sc.contribc.contribPath, "modeval::postConfig::contribPath"); + debug(sc.contribc.composName, "modeval::postConfig::composName"); + + // Count the calls to post config + const string k("tuscany::modeval::postConfig"); + const long int count = (long 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) { + // Chdir to the deployed contribution + if (chdir(c_str(sc.contribc.contribPath)) != 0) { + mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contribc.contribPath); + return -1; + } + + debug("modeval::postConfig::start"); + const failable<value> r = failableResult(applyLifecycle(mklist<value>("start"))); + if (!hasContent(r)) + return -1; + debug("modeval::postConfig::setlifecycle"); + 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; + debug("modeval::postConfig::setlifecycle"); + sc.lifecycle = content(r); + } + + // Configure the deployed components + const failable<Composite> compos = confComponents(sc.contribc.contribPath, sc.contribc.composName, value(), "", sc.compos.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); + if (!hasContent(compos)) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + return -1; + } + sc.compos = content(compos); + + // Register a cleanup callback, called when the server is stopped or restarted + apr_pool_pre_cleanup_register(p, (void*)&sc, serverCleanup); + + // Merge the configuration into the virtual hosts + return postConfigMerge(sc, s->next); +} + +/** + * Exit after a failure. + */ +void failureExitChild() { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* s) { + gc_scoped_pool pool(p); + + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval); + if(psc == NULL) + failureExitChild(); + ServerConf& sc = *psc; + + // Start the components in the child process + const failable<list<value> > fsimpls = startComponents(sc.compos.impls); + if (!hasContent(fsimpls)) + failureExitChild(); + sc.compos.impls = content(fsimpls); + + // Get the vhost contributor component implementation lambda + if (length(sc.vhostc.contributorName) != 0) { + const list<value> impl(assoctree<value>(sc.vhostc.contributorName, sc.compos.impls)); + if (isNil(impl)) { + mkfailure<int>(string("Couldn't find contributor component implementation: ") + sc.vhostc.contributorName); + failureExitChild(); + } + sc.vhostc.contributor = cadr<value>(impl); + } + + // Get the vhost authenticator component implementation lambda + if (length(sc.vhostc.authenticatorName) != 0) { + const list<value> impl(assoctree<value>(sc.vhostc.authenticatorName, sc.compos.impls)); + if (isNil(impl)) { + mkfailure<int>(string("Couldn't find authenticator component implementation: ") + sc.vhostc.authenticatorName); + failureExitChild(); + } + sc.vhostc.authenticator = cadr<value>(impl); + } + + // Merge the updated configuration into the virtual hosts + postConfigMerge(sc, s->next); + + // Register a cleanup callback, called when the child is stopped or restarted + apr_pool_pre_cleanup_register(p, (void*)psc, serverCleanup); +} + +/** + * 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_eval); + sc.contribc.contribPath = 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.contribc.composName = arg; + return NULL; +} +const char* confVirtualDomain(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.vhostc.domain = arg; + return NULL; +} +const char* confVirtualContribution(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.vhostc.contribPath = arg; + return NULL; +} +const char* confVirtualContributor(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.vhostc.contributorName = arg; + return NULL; +} +const char* confVirtualComposite(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.vhostc.composName = arg; + return NULL; +} +const char* confAuthenticator(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.vhostc.authenticatorName = 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.sslc.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.sslc.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.sslc.key = arg; + return NULL; +} +const char* confTimeout(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.timeout = atoi(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("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_TAKE1("SCAVirtualDomain", (const char*(*)())confVirtualDomain, NULL, RSRC_CONF, "SCA virtual host domain"), + AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution path"), + AP_INIT_TAKE1("SCAVirtualContributor", (const char*(*)())confVirtualContributor, NULL, RSRC_CONF, "SCA virtual host contributor component"), + AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"), + AP_INIT_TAKE1("SCAAuthenticator", (const char*(*)())confAuthenticator, NULL, RSRC_CONF, "SCA authenticator component"), + AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"), + AP_INIT_TAKE1("SCAWiringSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SCA wiring SSL CA certificate file"), + AP_INIT_TAKE1("SCAWiringSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SCA wiring SSL certificate file"), + AP_INIT_TAKE1("SCAWiringSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "SCA wiring SSL certificate key file"), + AP_INIT_TAKE1("SCAWiringTimeout", (const char*(*)())confTimeout, NULL, RSRC_CONF, "SCA wiring timeout"), + {NULL, NULL, NULL, 0, NO_ARGS, NULL} +}; + + +const authn_provider AuthnProvider = { + &checkPassword, + NULL +}; + +void retrieveAuthnCacheStore() { + authnCacheStore = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store); +} + +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_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "component", AUTHN_PROVIDER_VERSION, &AuthnProvider, AP_AUTH_INTERNAL_PER_CONF); + ap_hook_optional_fn_retrieve(retrieveAuthnCacheStore, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} +} + +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/lightweight-sca/modules/server/mod-scheme.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-scheme.hpp new file mode 100644 index 0000000000..43d7bf4041 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-scheme.hpp @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_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/lightweight-sca/modules/server/property-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/property-test.scm new file mode 100644 index 0000000000..f5ba76f1f3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/property-test.scm @@ -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. + +; Built-in property test case + +(define (print host path query) (list (list 'host (host)) (list 'path (path)) (list 'query (query)))) + diff --git a/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf b/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf new file mode 100755 index 0000000000..bc4074c8be --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf @@ -0,0 +1,37 @@ +#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +cat >>$root/conf/modules.conf <<EOF +# Generated by: scheme-conf $* +# Support for Scheme SCA components +LoadModule mod_tuscany_eval $here/libmod_tuscany_eval$libsuffix + +EOF diff --git a/sca-cpp/branches/lightweight-sca/modules/server/server-conf b/sca-cpp/branches/lightweight-sca/modules/server/server-conf new file mode 100755 index 0000000000..bfa5ac8473 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/server-conf @@ -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. + +# Generate a server conf +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +# Serve Javascript scripts and CSS +$here/../js/js-conf $1 + +# Configure SSL cert used for wiring +ssl=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$ssl" != "" ]; then + cat >>$root/conf/httpd.conf <<EOF +# Configure SSL certificates +SCAWiringSSLCACertificateFile "$root/cert/ca.crt" +SCAWiringSSLCertificateFile "$root/cert/server.crt" +SCAWiringSSLCertificateKeyFile "$root/cert/server.key" + +EOF + +fi + diff --git a/sca-cpp/branches/lightweight-sca/modules/server/server-test b/sca-cpp/branches/lightweight-sca/modules/server/server-test new file mode 100755 index 0000000000..6b48d13e83 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/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 +rm -rf tmp +../http/httpd-conf tmp localhost 8090 htdocs +../http/httpd-event-conf tmp +./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 +exit $rc diff --git a/sca-cpp/branches/lightweight-sca/modules/server/server-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/server-test.scm new file mode 100644 index 0000000000..4bbff6e5c2 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/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) + '((feed (title "Sample Feed") (id "123456789") (entry + (((title "Item") (id "111") (content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))) + ((title "Item") (id "222") (content (item (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55)))) + ((title "Item") (id "333") (content (item (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55)))))))) + + (list (list 'entry '(title "Item") (list 'id (car id)) '(content (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/lightweight-sca/modules/server/wiring-test b/sca-cpp/branches/lightweight-sca/modules/server/wiring-test new file mode 100755 index 0000000000..7e1aea22b1 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/modules/server/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +curl_prefix=`cat $here/../http/curl.prefix` + +# Setup +rm -rf tmp +../http/httpd-conf tmp localhost 8090 htdocs +../http/httpd-event-conf tmp +./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/test/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/test/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/test/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/test/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/test/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/test/json-result.txt + rc=$? +fi + +# Cleanup +../http/httpd-stop tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +exit $rc |