diff options
Diffstat (limited to '')
23 files changed, 1249 insertions, 123 deletions
diff --git a/sca-cpp/trunk/configure.ac b/sca-cpp/trunk/configure.ac index 38a529ed21..b94e540e51 100644 --- a/sca-cpp/trunk/configure.ac +++ b/sca-cpp/trunk/configure.ac @@ -859,6 +859,7 @@ AC_CONFIG_FILES([Makefile samples/store-gae/Makefile samples/store-sql/Makefile samples/store-nosql/Makefile + samples/store-vhost/Makefile doc/Makefile doc/Doxyfile ubuntu/Makefile diff --git a/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf index 2ef922145f..a6ba048f67 100755 --- a/sca-cpp/trunk/modules/http/httpd-conf +++ b/sca-cpp/trunk/modules/http/httpd-conf @@ -30,6 +30,13 @@ htdocs=`readlink -f $4` user=`id -un` group=`id -gn` modules_prefix=`cat $here/httpd-modules.prefix` +if [ "$5" = "vhost" ]; then + vhost="VirtualDocumentRoot $htdocs/domains/%1/" + maxr="MaxRequestsPerChild 1" +else + vhost="#VirtualDocumentRoot $htdocs/domains/%1/" + maxr="#MaxRequestsPerChild 1" +fi mkdir -p $root mkdir -p $root/logs @@ -39,8 +46,6 @@ cat >$root/conf/httpd.conf <<EOF # Set server name ServerName http://$host:$pport -UseCanonicalName On -UseCanonicalPhysicalPort off PidFile $root/logs/httpd.pid # Minimal set of modules @@ -68,6 +73,7 @@ LoadModule setenvif_module ${modules_prefix}/modules/mod_setenvif.so <IfModule !log_config_module> LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so </IfModule> +LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so # Basic security precautions User $user @@ -121,7 +127,15 @@ Allow from all Listen $port <VirtualHost _default_:$port> ServerName http://$host:$pport +UseCanonicalName Off +UseCanonicalPhysicalPort Off + +# Setup mass dynamic virtual hosting +$vhost </VirtualHost> +# Isolate dynamic virtual hosts +$maxr + EOF diff --git a/sca-cpp/trunk/modules/http/httpd-ssl-conf b/sca-cpp/trunk/modules/http/httpd-ssl-conf index df1d8042bc..04ca61dba0 100755 --- a/sca-cpp/trunk/modules/http/httpd-ssl-conf +++ b/sca-cpp/trunk/modules/http/httpd-ssl-conf @@ -55,8 +55,8 @@ SSLRandomSeed connect builtin Listen $sslport <VirtualHost _default_:$sslport> ServerName https://$host:$sslpport -UseCanonicalName On -UseCanonicalPhysicalPort off +UseCanonicalName Off +UseCanonicalPhysicalPort Off # Enable SSL SSLEngine on diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp index 93137be4e2..bd4f6e8ada 100644 --- a/sca-cpp/trunk/modules/http/httpd.hpp +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -75,6 +75,31 @@ template<typename C> C& serverConf(const cmd_parms *cmd, const module* mod) { /** + * Return the name of a server. + */ +const string serverName(const server_rec* s) { + ostringstream n; + n << (s->server_scheme != NULL? s->server_scheme : "http") << "://" + << (s->server_hostname != NULL? s->server_hostname : "localhost") << ":" + << (s->port != 0? s->port : 80) + << (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) { + ostringstream n; + const char* hn = ap_get_server_name(r); + n << (r->server->server_scheme != NULL? r->server->server_scheme : "http") << "://" + << (hn != NULL? hn : (r->server->server_hostname != NULL? r->server->server_hostname : "localhost")) << ":" + << (r->server->port != 0? r->server->port : 80) + << (r->server->path != NULL? string(r->server->path, r->server->pathlen) : ""); + return str(n); +} + +/** * Return the content type of a request. */ const char* optional(const char* s) { @@ -295,7 +320,7 @@ const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) { } /** - * Create an HTTPD redirect request. + * Create an HTTPD internal redirect request. * Similar to httpd/modules/http/http_request.c::internal_internal_redirect. */ extern "C" { @@ -388,7 +413,7 @@ const int internalRedirect(request_rec* nr) { } /** - * Create and process an HTTPD redirect request. + * Create and process an HTTPD internal redirect request. */ const int internalRedirect(const string& uri, request_rec* r) { const failable<request_rec*, int> nr = httpd::internalRedirectRequest(uri, r); diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp index 14e5f40e9b..464e0eabf1 100644 --- a/sca-cpp/trunk/modules/server/mod-eval.hpp +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -53,14 +53,16 @@ namespace modeval { */ class ServerConf { public: - ServerConf(server_rec* s) : s(s), wiringServerName(""), contributionPath(""), compositeName(""), ca(""), cert(""), key("") { + ServerConf(server_rec* s) : server(s), wiringServerName(""), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName(""), ca(""), cert(""), key("") { } - const server_rec* s; + server_rec* server; lambda<value(const list<value>&)> lifecycle; string wiringServerName; string contributionPath; string compositeName; + string virtualHostContributionPath; + string virtualHostCompositeName; string ca; string cert; string key; @@ -69,6 +71,13 @@ public: }; /** + * Return true if a server contains a composite configuration. + */ +const bool hasCompositeConf(const ServerConf& sc) { + return sc.contributionPath != "" && sc.compositeName != ""; +} + +/** * Convert a result represented as a content + failure pair to a * failable monad. */ @@ -241,6 +250,19 @@ int translate(request_rec *r) { } /** + * Convert a list of component references to a list of HTTP proxy lambdas. + */ +const value mkrefProxy(const value& ref, const string& base, const string& ca, const string& cert, const string& key) { + return lambda<value(const list<value>&)>(http::proxy(base + string(scdl::name(ref)), ca, cert, key)); +} + +const list<value> refProxies(const list<value>& refs, const string& base, const string& ca, const string& cert, const string& key) { + if (isNil(refs)) + return refs; + return cons(mkrefProxy(car(refs), base, ca, cert, key), refProxies(cdr(refs), base, ca, cert, key)); +} + +/** * Store current HTTP request for access from property lambda functions. */ #ifdef WANT_THREADS @@ -248,63 +270,18 @@ __thread #endif const request_rec* currentRequest = NULL; -class scoped_request { +class ScopedRequest { public: - scoped_request(const request_rec* r) { + ScopedRequest(const request_rec* r) { currentRequest = r; } - ~scoped_request() { + ~ScopedRequest() { currentRequest = NULL; } }; /** - * HTTP request handler. - */ -int handler(request_rec *r) { - if(strcmp(r->handler, "mod_tuscany_eval")) - return DECLINED; - gc_scoped_pool pool(r->pool); - scoped_request sr(r); - httpdDebugRequest(r, "modeval::handler::input"); - - // Get the component implementation lambda - const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); - const list<value> path(pathValues(r->uri)); - const list<value> impl(assoctree<value>(cadr(path), sc.implTree)); - if (isNil(impl)) - return httpd::reportStatus(mkfailure<int>(string("Couldn't find component implementation"))); - - // Handle HTTP method - const lambda<value(const list<value>&)> l(cadr<value>(impl)); - if (r->header_only) - return OK; - if(r->method_number == M_GET) - return httpd::reportStatus(get(r, l)); - if(r->method_number == M_POST) - return httpd::reportStatus(post(r, l)); - if(r->method_number == M_PUT) - return httpd::reportStatus(put(r, l)); - if(r->method_number == M_DELETE) - return httpd::reportStatus(del(r, l)); - return HTTP_NOT_IMPLEMENTED; -} - -/** - * Convert a list of component references to a list of HTTP proxy lambdas. - */ -const value mkrefProxy(const value& ref, const string& base, const string& ca, const string& cert, const string& key) { - return lambda<value(const list<value>&)>(http::proxy(base + string(scdl::name(ref)), ca, cert, key)); -} - -const list<value> refProxies(const list<value>& refs, const string& base, const string& ca, const string& cert, const string& key) { - if (isNil(refs)) - return refs; - return cons(mkrefProxy(car(refs), base, ca, cert, key), refProxies(cdr(refs), base, ca, cert, key)); -} - -/** * Convert a list of component properties to a list of lambda functions that just return * the property value. The user and email properties are configured with the values * from the HTTP request, if any. @@ -374,20 +351,14 @@ struct implementationFailure { } }; -const value evalComponent(ServerConf& sc, server_rec& server, const value& comp) { +const value evalComponent(ServerConf& sc, const value& comp) { extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle); const value impl = scdl::implementation(comp); // Convert component references to configured proxy lambdas ostringstream base; - if (sc.wiringServerName == "") - base << (server.server_scheme == NULL? "http" : server.server_scheme) - << "://" << (server.server_hostname == NULL? "localhost" : server.server_hostname) - << ":" << (server.port == 0? 80 : server.port) - << "/references/" << string(scdl::name(comp)) << "/"; - else - base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; + base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; const list<value> rpx(refProxies(scdl::references(comp), str(base), sc.ca, sc.cert, sc.key)); // Convert component proxies to configured proxy lambdas @@ -403,10 +374,10 @@ const value evalComponent(ServerConf& sc, server_rec& server, const value& comp) /** * Return a list of component-name + configured-implementation pairs. */ -const list<value> componentToImplementationAssoc(ServerConf& sc, server_rec& server, const list<value>& c) { +const list<value> componentToImplementationAssoc(ServerConf& sc, const list<value>& c) { if (isNil(c)) return c; - return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, server, car(c))), componentToImplementationAssoc(sc, server, cdr(c))); + return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, car(c))), componentToImplementationAssoc(sc, cdr(c))); } /** @@ -447,8 +418,8 @@ const failable<list<value> > applyLifecycleExpr(const list<value>& impls, const /** * Configure the components declared in the deployed composite. */ -const failable<bool> confComponents(ServerConf& sc, server_rec& server) { - if (sc.contributionPath == "" || sc.compositeName == "") +const failable<bool> confComponents(ServerConf& sc) { + if (!hasCompositeConf(sc)) return false; // Chdir to the deployed contribution @@ -459,7 +430,7 @@ const failable<bool> confComponents(ServerConf& sc, server_rec& server) { const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName); if (!hasContent(comps)) return mkfailure<bool>(reason(comps)); - sc.implementations = componentToImplementationAssoc(sc, server, content(comps)); + sc.implementations = componentToImplementationAssoc(sc, content(comps)); debug(sc.implementations, "modeval::confComponents::implementations"); // Store the implementation lambda functions in a tree for fast retrieval @@ -486,6 +457,107 @@ const failable<bool> startComponents(ServerConf& sc) { } /** + * Virtual host scoped server configuration. + */ +class VirtualHostConf { +public: + VirtualHostConf(const ServerConf& ssc) : sc(ssc.server) { + sc.contributionPath = ssc.virtualHostContributionPath; + sc.compositeName = ssc.virtualHostCompositeName; + sc.wiringServerName = ssc.wiringServerName; + sc.ca = ssc.ca; + sc.cert = ssc.cert; + sc.key = ssc.key; + } + + ~VirtualHostConf() { + extern const failable<bool> virtualHostCleanup(const ServerConf& sc); + virtualHostCleanup(sc); + } + + ServerConf sc; +}; + +/** + * Configure and start the components deployed in a virtual host. + */ +const failable<bool> virtualHostConfig(ServerConf& sc, request_rec* r) { + extern const value applyLifecycle(const list<value>&); + + // Determine the server name and wiring server name + debug(httpd::serverName(sc.server), "modeval::virtualHostConfig::serverName"); + debug(httpd::serverName(r), "modeval::virtualHostConfig::virtualHostName"); + sc.wiringServerName = httpd::serverName(r); + debug(sc.wiringServerName, "modeval::virtualHostConfig::wiringServerName"); + + // Configure the deployed components + debug(sc.contributionPath, "modeval::virtualHostConfig::contributionPath"); + debug(sc.compositeName, "modeval::virtualHostConfig::compositeName"); + const failable<bool> cr = confComponents(sc); + if (!hasContent(cr)) + return cr; + + // Start the configured components + return startComponents(sc); +} + +/** + * Cleanup a virtual host. + */ +const failable<bool> virtualHostCleanup(const ServerConf& sc) { + if (!hasCompositeConf(sc)) + return true; + debug("modeval::virtualHostCleanup"); + + // Stop the component implementations + applyLifecycleExpr(sc.implementations, mklist<value>("stop")); + return true; +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + gc_scoped_pool pool(r->pool); + ScopedRequest sr(r); + httpdDebugRequest(r, "modeval::handler::input"); + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + + // Process dynamic virtual host configuration, if any + VirtualHostConf vhc(sc); + const bool hasv = hasCompositeConf(vhc.sc); + if (hasv) { + const failable<bool> cr = virtualHostConfig(vhc.sc, r); + if (!hasContent(cr)) + return httpd::reportStatus(mkfailure<int>(reason(cr))); + } + + // Get the component implementation lambda + const list<value> path(pathValues(r->uri)); + const list<value> impl(assoctree<value>(cadr(path), hasv? vhc.sc.implTree : sc.implTree)); + if (isNil(impl)) + return httpd::reportStatus(mkfailure<int>(string("Couldn't find component implementation"))); + + // Handle HTTP method + const lambda<value(const list<value>&)> l(cadr<value>(impl)); + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return httpd::reportStatus(get(r, l)); + if(r->method_number == M_POST) + return httpd::reportStatus(post(r, l)); + if(r->method_number == M_PUT) + return httpd::reportStatus(put(r, l)); + if(r->method_number == M_DELETE) + return httpd::reportStatus(del(r, l)); + return HTTP_NOT_IMPLEMENTED; +} + +/** * Cleanup callback, called when the server is stopped or restarted. */ apr_status_t serverCleanup(void* v) { @@ -512,10 +584,14 @@ apr_status_t serverCleanup(void* v) { const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { if (s == NULL) return OK; + ostringstream sname; + debug(httpd::serverName(s), "modeval::postConfigMerge::serverName"); ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); sc.wiringServerName = mainsc.wiringServerName; sc.contributionPath = mainsc.contributionPath; sc.compositeName = mainsc.compositeName; + sc.virtualHostContributionPath = mainsc.virtualHostContributionPath; + sc.virtualHostCompositeName = mainsc.virtualHostCompositeName; sc.ca = mainsc.ca; sc.cert = mainsc.cert; sc.key = mainsc.key; @@ -528,7 +604,13 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, extern const value applyLifecycle(const list<value>&); gc_scoped_pool pool(p); + + // Get the server configuration and determine the wiring server name ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval); + debug(httpd::serverName(s), "modeval::postConfig::serverName"); + if (sc.wiringServerName == "") + sc.wiringServerName = httpd::serverName(s); + debug(sc.wiringServerName, "modeval::postConfig::wiringServerName"); // Count the calls to post config const string k("tuscany::modeval::postConfig"); @@ -539,6 +621,7 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, // count == 1 is the first start, count > 1 is a restart if (count == 0) return OK; + if (count == 1) { debug("modeval::postConfig::start"); const failable<value> r = failableResult(applyLifecycle(mklist<value>("start"))); @@ -555,10 +638,9 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, } // Configure the deployed components - debug(sc.wiringServerName, "modeval::postConfig::wiringServerName"); debug(sc.contributionPath, "modeval::postConfig::contributionPath"); debug(sc.compositeName, "modeval::postConfig::compositeName"); - const failable<bool> res = confComponents(sc, *s); + const failable<bool> res = confComponents(sc); if (!hasContent(res)) { cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; return -1; @@ -614,6 +696,18 @@ const char* confComposite(cmd_parms *cmd, unused void *c, const char *arg) { sc.compositeName = 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.virtualHostContributionPath = 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.virtualHostCompositeName = 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); @@ -646,6 +740,8 @@ const command_rec commands[] = { AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"), AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"), AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"), + AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"), + AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"), AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"), AP_INIT_TAKE1("SCASSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SSL CA certificate file"), AP_INIT_TAKE1("SCASSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SSL certificate file"), diff --git a/sca-cpp/trunk/modules/server/mod-wiring.cpp b/sca-cpp/trunk/modules/server/mod-wiring.cpp index 7f41a7e03d..5b2aba73b3 100644 --- a/sca-cpp/trunk/modules/server/mod-wiring.cpp +++ b/sca-cpp/trunk/modules/server/mod-wiring.cpp @@ -44,23 +44,32 @@ namespace server { namespace modwiring { /** + * Set to true to wire using mod_proxy, false to wire using HTTP client redirects. + */ +const bool useModProxy = true; + +/** * Server configuration. */ class ServerConf { public: - ServerConf(server_rec* s) : s(s), contributionPath(""), compositeName("") { + ServerConf(server_rec* s) : server(s), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName("") { } - const server_rec* s; + server_rec* server; string contributionPath; string compositeName; + string virtualHostContributionPath; + string virtualHostCompositeName; list<value> references; list<value> services; }; /** - * Set to true to wire using mod_proxy, false to wire using HTTP client redirects. + * Return true if a server contains a composite configuration. */ -const bool useModProxy = true; +const bool hasCompositeConf(const ServerConf& sc) { + return sc.contributionPath != "" && sc.compositeName != ""; +} /** * Returns true if a URI is absolute. @@ -73,12 +82,11 @@ const bool isAbsolute(const string& uri) { * Route a /references/component-name/reference-name request, * to the target of the component reference. */ -int translateReference(request_rec *r) { +int translateReference(const ServerConf& sc, request_rec *r) { httpdDebugRequest(r, "modwiring::translateReference::input"); debug(r->uri, "modwiring::translateReference::uri"); // Find the requested component - const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring); const list<value> rpath(pathValues(r->uri)); const list<value> comp(assoctree(cadr(rpath), sc.references)); if (isNil(comp)) @@ -138,12 +146,11 @@ const list<value> assocPath(const value& k, const list<value>& tree) { /** * Route a service request to the component providing the requested service. */ -int translateService(request_rec *r) { +int translateService(const ServerConf& sc, request_rec *r) { httpdDebugRequest(r, "modwiring::translateService::input"); debug(r->uri, "modwiring::translateService::uri"); // Find the requested component - const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring); debug(sc.services, "modwiring::translateService::services"); const list<value> p(pathValues(r->uri)); const list<value> svc(assocPath(p, sc.services)); @@ -166,44 +173,6 @@ int translateService(request_rec *r) { } /** - * Translate an HTTP service or reference request and route it - * to the target component. - */ -int translate(request_rec *r) { - gc_scoped_pool pool(r->pool); - if (!strncmp(r->uri, "/components/", 12) != 0) - return DECLINED; - - // Translate a component reference request - if (!strncmp(r->uri, "/references/", 12) != 0) - return translateReference(r); - - // Translate a service request - return translateService(r); -} - -/** - * HTTP request handler, redirect to a target component. - */ -int handler(request_rec *r) { - gc_scoped_pool pool(r->pool); - if(strcmp(r->handler, "mod_tuscany_wiring")) - return DECLINED; - httpdDebugRequest(r, "modwiring::handler::input"); - - // Do an internal redirect - if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) - return DECLINED; - debug(r->uri, "modwiring::handler::uri"); - debug(r->filename, "modwiring::handler::filename"); - debug(r->path_info, "modwiring::handler::path info"); - - if (r->args == NULL) - return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r); - return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r); -} - -/** * Read the components declared in a composite. */ const failable<list<value> > readComponents(const string& path) { @@ -264,7 +233,7 @@ const list<value> uriToComponentAssoc(const list<value>& c) { * Configure the components declared in the server's deployment composite. */ const bool confComponents(ServerConf& sc) { - if (sc.contributionPath == "" || sc.compositeName == "") + if (!hasCompositeConf(sc)) return true; // Read the component configuration and store the references and service URIs @@ -283,21 +252,109 @@ const bool confComponents(ServerConf& sc) { } /** + * Virtual host scoped server configuration. + */ +class VirtualHostConf { +public: + VirtualHostConf(const ServerConf& ssc) : sc(ssc.server) { + sc.contributionPath = ssc.virtualHostContributionPath; + sc.compositeName = ssc.virtualHostCompositeName; + } + + ~VirtualHostConf() { + } + + ServerConf sc; +}; + +/** + * Configure and start the components deployed in a virtual host. + */ +const failable<bool> virtualHostConfig(ServerConf& sc, request_rec* r) { + debug(httpd::serverName(sc.server), "modwiring::virtualHostConfig::serverName"); + debug(httpd::serverName(r), "modwiring::virtualHostConfig::virtualHostName"); + + // Configure the wiring for the deployed components + debug(sc.contributionPath, "modwiring::virtualHostConfig::contributionPath"); + debug(sc.compositeName, "modwiring::virtualHostConfig::compositeName"); + confComponents(sc); + return true; +} + +/** + * Translate an HTTP service or reference request and route it + * to the target component. + */ +int translate(request_rec *r) { + gc_scoped_pool pool(r->pool); + + // No translation needed for a component request + if (!strncmp(r->uri, "/components/", 12)) + return DECLINED; + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring); + + // Process dynamic virtual host configuration, if any + VirtualHostConf vhc(sc); + const bool hasv = hasCompositeConf(vhc.sc); + if (hasv) { + const failable<bool> cr = virtualHostConfig(vhc.sc, r); + if (!hasContent(cr)) + return -1; + } + + // Translate a component reference request + if (!strncmp(r->uri, "/references/", 12)) + return translateReference(hasv? vhc.sc: sc, r); + + // Translate a service request + return translateService(hasv? vhc.sc : sc, r); +} + +/** + * HTTP request handler, redirect to a target component. + */ +int handler(request_rec *r) { + gc_scoped_pool pool(r->pool); + if(strcmp(r->handler, "mod_tuscany_wiring")) + return DECLINED; + httpdDebugRequest(r, "modwiring::handler::input"); + + // Do an internal redirect + if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) + return DECLINED; + debug(r->uri, "modwiring::handler::uri"); + debug(r->filename, "modwiring::handler::filename"); + debug(r->path_info, "modwiring::handler::path info"); + + if (r->args == NULL) + return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r); + return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r); +} + +/** * Called after all the configuration commands have been run. * Process the server configuration and configure the wiring for the deployed components. */ const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { if (s == NULL) return OK; + ostringstream sname; + debug(httpd::serverName(s), "modwiring::postConfigMerge::serverName"); ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring); sc.contributionPath = mainsc.contributionPath; sc.compositeName = mainsc.compositeName; + sc.virtualHostContributionPath = mainsc.virtualHostContributionPath; + sc.virtualHostCompositeName = mainsc.virtualHostCompositeName; sc.references = mainsc.references; sc.services = mainsc.services; return postConfigMerge(mainsc, s->next); } int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) { + gc_scoped_pool pool(p); + // Count the calls to post config, skip the first one as // postConfig is always called twice const string k("tuscany::modwiring::postConfig"); @@ -308,6 +365,7 @@ int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t // Configure the wiring for the deployed components ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring); + debug(httpd::serverName(s), "modwiring::postConfig::serverName"); debug(sc.contributionPath, "modwiring::postConfig::contributionPath"); debug(sc.compositeName, "modwiring::postConfig::compositeName"); confComponents(sc); @@ -319,9 +377,9 @@ int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t /** * Child process initialization. */ -void childInit(apr_pool_t* p, server_rec* svr_rec) { +void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); - ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring); + ServerConf *conf = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_wiring); if(conf == NULL) { cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << endl; exit(APEXIT_CHILDFATAL); @@ -343,6 +401,18 @@ const char *confComposite(cmd_parms *cmd, unused void *c, const char *arg) { sc.compositeName = 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_wiring); + sc.virtualHostContributionPath = 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_wiring); + sc.virtualHostCompositeName = arg; + return NULL; +} /** * HTTP server module declaration. @@ -350,6 +420,8 @@ const char *confComposite(cmd_parms *cmd, unused void *c, const char *arg) { 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("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"), + AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual host composite location"), {NULL, NULL, NULL, 0, NO_ARGS, NULL} }; diff --git a/sca-cpp/trunk/modules/server/server-conf b/sca-cpp/trunk/modules/server/server-conf index 8ab6934d00..cff1ab1965 100755 --- a/sca-cpp/trunk/modules/server/server-conf +++ b/sca-cpp/trunk/modules/server/server-conf @@ -39,8 +39,9 @@ fi cat >>$root/conf/httpd.conf <<EOF # Support for SCA component wiring LoadModule mod_tuscany_wiring $here/libmod_tuscany_wiring.so -SCAWiringServerName $host:$port +#SCAWiringServerName $host:$port +# Serve HTTP binding JavaScript client code Alias /js/tuscany-ref.js $here/htdocs/js/tuscany-ref.js EOF diff --git a/sca-cpp/trunk/samples/Makefile.am b/sca-cpp/trunk/samples/Makefile.am index e2b74075f8..ff212b7eab 100644 --- a/sca-cpp/trunk/samples/Makefile.am +++ b/sca-cpp/trunk/samples/Makefile.am @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -SUBDIRS = store-scheme store-cpp store-python store-java store-gae store-sql store-nosql +SUBDIRS = store-scheme store-cpp store-python store-java store-gae store-sql store-nosql store-vhost sample_DATA = README sampledir=$(prefix)/samples diff --git a/sca-cpp/trunk/samples/store-python/Makefile.am b/sca-cpp/trunk/samples/store-python/Makefile.am index fe389e7eed..7a38fa50f3 100644 --- a/sca-cpp/trunk/samples/store-python/Makefile.am +++ b/sca-cpp/trunk/samples/store-python/Makefile.am @@ -17,7 +17,7 @@ if WANT_PYTHON -dist_sample_SCRIPTS = start stop ssl-start +dist_sample_SCRIPTS = start stop ssl-start uec2-start sampledir = $(prefix)/samples/store-python nobase_dist_sample_DATA = currency-converter.py fruits-catalog.py shopping-cart.py store.py store.composite htdocs/*.html diff --git a/sca-cpp/trunk/samples/store-vhost/Makefile.am b/sca-cpp/trunk/samples/store-vhost/Makefile.am new file mode 100644 index 0000000000..61933afd35 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/Makefile.am @@ -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. + +if WANT_PYTHON + +dist_sample_SCRIPTS = start stop ssl-start uec2-start +sampledir = $(prefix)/samples/store-vhost + +nobase_dist_sample_DATA = currency-converter.py fruits-catalog.py shopping-cart.py store.py store.composite htdocs/*.html htdocs/domains/joe/*.html htdocs/domains/jane/*.html + +dist_noinst_SCRIPTS = server-test +#TESTS = server-test + +endif diff --git a/sca-cpp/trunk/samples/store-vhost/currency-converter.py b/sca-cpp/trunk/samples/store-vhost/currency-converter.py new file mode 100644 index 0000000000..2fded8f616 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/currency-converter.py @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Currency converter implementation + +def convert(fr, to, amount): + if to == "EUR": + return amount * 0.70 + return amount + +def symbol(currency): + if currency == "EUR": + return "E" + return "$" + diff --git a/sca-cpp/trunk/samples/store-vhost/fruits-catalog.py b/sca-cpp/trunk/samples/store-vhost/fruits-catalog.py new file mode 100644 index 0000000000..2a6d726fdc --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/fruits-catalog.py @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Catalog implementation + +def getcatalog(converter, currencyCode): + code = currencyCode() + def convert(price): + return converter("convert", "USD", code, price) + symbol = converter("symbol", code) + return ( + (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", code), ("'currencySymbol", symbol), ("'price", convert(2.99))), + (("'javaClass", "services.Item"), ("'name", "Orange"), ("'currencyCode", code), ("'currencySymbol", symbol), ("'price", convert(3.55))), + (("'javaClass", "services.Item"), ("'name", "Pear"), ("'currencyCode", code), ("'currencySymbol", symbol), ("'price", convert(1.55))) + ) + diff --git a/sca-cpp/trunk/samples/store-vhost/htdocs/domains/jane/index.html b/sca-cpp/trunk/samples/store-vhost/htdocs/domains/jane/index.html new file mode 100644 index 0000000000..9fc3247e22 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/htdocs/domains/jane/index.html @@ -0,0 +1,157 @@ +<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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>
+<title>Store</title>
+
+<script type="text/javascript" src="/js/tuscany-ref.js"></script>
+
+<script type="text/javascript">
+var component = new tuscany.sca.Component("Store");
+
+//@Reference
+var catalog = new tuscany.sca.Reference("catalog");
+
+//@Reference
+var shoppingCart = new tuscany.sca.Reference("shoppingCart");
+
+//@Reference
+var shoppingTotal = new tuscany.sca.Reference("shoppingTotal");
+
+var catalogItems;
+
+function catalog_getcatalogResponse(items, exception) {
+ if (exception){
+ alert(exception.message);
+ return;
+ }
+ var catalog = "";
+ for (var i=0; i<items.length; i++) {
+ var item = items[i].name + ' - ' + items[i].price;
+ catalog += '<input name="items" type="checkbox" value="' +
+ item + '">' + item + ' <br>';
+ }
+ document.getElementById('catalog').innerHTML=catalog;
+ catalogItems = items;
+
+}
+
+function shoppingCart_getResponse(feed) {
+ if (feed != null) {
+ var entries = feed.getElementsByTagName("entry");
+ var list = "";
+ for (var i=0; i<entries.length; i++) {
+ var content = entries[i].getElementsByTagName("content")[0];
+ var name = content.getElementsByTagName("name")[0].firstChild.nodeValue;
+ var price = content.getElementsByTagName("price")[0].firstChild.nodeValue;
+ list += name + ' - ' + price + ' <br>';
+ }
+ document.getElementById("shoppingCart").innerHTML = list;
+
+ shoppingTotal.apply("gettotal", shoppingTotal_gettotalResponse);
+ }
+}
+
+function shoppingTotal_gettotalResponse(total, exception) {
+ if (exception) {
+ alert(exception.message);
+ return;
+ }
+ document.getElementById('total').innerHTML = total;
+}
+
+function shoppingCart_postResponse(entry) {
+ shoppingCart.get("", shoppingCart_getResponse);
+}
+
+function addToCart() {
+ var items = document.catalogForm.items;
+ var j = 0;
+ for (var i=0; i<items.length; i++)
+ if (items[i].checked) {
+ var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><content type="application/xml">' +
+ '<item>' +
+ '<javaClass>' + catalogItems[i].javaClass + '</javaClass>' +
+ '<name>' + catalogItems[i].name + '</name>' +
+ '<currencyCode>' + catalogItems[i].currencyCode + '</currencyCode>' +
+ '<currencySymbol>' + catalogItems[i].currencySymbol + '</currencySymbol>' +
+ '<price>' + catalogItems[i].price + '</price>' +
+ '</item>' +
+ '</content></entry>';
+ shoppingCart.post(entry, shoppingCart_postResponse);
+ items[i].checked = false;
+ }
+}
+function checkoutCart() {
+ document.getElementById('store').innerHTML='<h2>' +
+ 'Thanks for Shopping With Us!</h2>'+
+ '<h2>Your Order</h2>'+
+ '<form name="orderForm">'+
+ document.getElementById('shoppingCart').innerHTML+
+ '<br>'+
+ document.getElementById('total').innerHTML+
+ '<br>'+
+ '<br>'+
+ '<input type="submit" value="Continue Shopping">'+
+ '</form>';
+ shoppingCart.del("", null);
+}
+function deleteCart() {
+ shoppingCart.del("", null);
+ document.getElementById('shoppingCart').innerHTML = "";
+ document.getElementById('total').innerHTML = "";
+}
+
+function init() {
+ try {
+ catalog.apply("getcatalog", catalog_getcatalogResponse);
+ shoppingCart.get("", shoppingCart_getResponse);
+ } catch(e){
+ alert(e);
+ }
+}
+</script>
+
+</head>
+
+<body onload="init()">
+<h1>Jane's Store</h1>
+<div id="store">
+<h2>Catalog</h2>
+<form name="catalogForm">
+<div id="catalog" ></div>
+<br>
+<input type="button" onClick="addToCart()" value="Add to Cart">
+</form>
+<br>
+
+<h2>Your Shopping Cart</h2>
+<form name="shoppingCartForm">
+<div id="shoppingCart"></div>
+<br>
+<div id="total"></div>
+<br>
+<input type="button" onClick="checkoutCart()" value="Checkout">
+<input type="button" onClick="deleteCart()" value="Empty">
+<a href="shoppingCart/">(feed)</a>
+</form>
+</div>
+
+</body>
+</html>
diff --git a/sca-cpp/trunk/samples/store-vhost/htdocs/domains/joe/index.html b/sca-cpp/trunk/samples/store-vhost/htdocs/domains/joe/index.html new file mode 100644 index 0000000000..e8b722d0d6 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/htdocs/domains/joe/index.html @@ -0,0 +1,157 @@ +<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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>
+<title>Store</title>
+
+<script type="text/javascript" src="/js/tuscany-ref.js"></script>
+
+<script type="text/javascript">
+var component = new tuscany.sca.Component("Store");
+
+//@Reference
+var catalog = new tuscany.sca.Reference("catalog");
+
+//@Reference
+var shoppingCart = new tuscany.sca.Reference("shoppingCart");
+
+//@Reference
+var shoppingTotal = new tuscany.sca.Reference("shoppingTotal");
+
+var catalogItems;
+
+function catalog_getcatalogResponse(items, exception) {
+ if (exception){
+ alert(exception.message);
+ return;
+ }
+ var catalog = "";
+ for (var i=0; i<items.length; i++) {
+ var item = items[i].name + ' - ' + items[i].price;
+ catalog += '<input name="items" type="checkbox" value="' +
+ item + '">' + item + ' <br>';
+ }
+ document.getElementById('catalog').innerHTML=catalog;
+ catalogItems = items;
+
+}
+
+function shoppingCart_getResponse(feed) {
+ if (feed != null) {
+ var entries = feed.getElementsByTagName("entry");
+ var list = "";
+ for (var i=0; i<entries.length; i++) {
+ var content = entries[i].getElementsByTagName("content")[0];
+ var name = content.getElementsByTagName("name")[0].firstChild.nodeValue;
+ var price = content.getElementsByTagName("price")[0].firstChild.nodeValue;
+ list += name + ' - ' + price + ' <br>';
+ }
+ document.getElementById("shoppingCart").innerHTML = list;
+
+ shoppingTotal.apply("gettotal", shoppingTotal_gettotalResponse);
+ }
+}
+
+function shoppingTotal_gettotalResponse(total, exception) {
+ if (exception) {
+ alert(exception.message);
+ return;
+ }
+ document.getElementById('total').innerHTML = total;
+}
+
+function shoppingCart_postResponse(entry) {
+ shoppingCart.get("", shoppingCart_getResponse);
+}
+
+function addToCart() {
+ var items = document.catalogForm.items;
+ var j = 0;
+ for (var i=0; i<items.length; i++)
+ if (items[i].checked) {
+ var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><content type="application/xml">' +
+ '<item>' +
+ '<javaClass>' + catalogItems[i].javaClass + '</javaClass>' +
+ '<name>' + catalogItems[i].name + '</name>' +
+ '<currencyCode>' + catalogItems[i].currencyCode + '</currencyCode>' +
+ '<currencySymbol>' + catalogItems[i].currencySymbol + '</currencySymbol>' +
+ '<price>' + catalogItems[i].price + '</price>' +
+ '</item>' +
+ '</content></entry>';
+ shoppingCart.post(entry, shoppingCart_postResponse);
+ items[i].checked = false;
+ }
+}
+function checkoutCart() {
+ document.getElementById('store').innerHTML='<h2>' +
+ 'Thanks for Shopping With Us!</h2>'+
+ '<h2>Your Order</h2>'+
+ '<form name="orderForm">'+
+ document.getElementById('shoppingCart').innerHTML+
+ '<br>'+
+ document.getElementById('total').innerHTML+
+ '<br>'+
+ '<br>'+
+ '<input type="submit" value="Continue Shopping">'+
+ '</form>';
+ shoppingCart.del("", null);
+}
+function deleteCart() {
+ shoppingCart.del("", null);
+ document.getElementById('shoppingCart').innerHTML = "";
+ document.getElementById('total').innerHTML = "";
+}
+
+function init() {
+ try {
+ catalog.apply("getcatalog", catalog_getcatalogResponse);
+ shoppingCart.get("", shoppingCart_getResponse);
+ } catch(e){
+ alert(e);
+ }
+}
+</script>
+
+</head>
+
+<body onload="init()">
+<h1>Joe's Store</h1>
+<div id="store">
+<h2>Catalog</h2>
+<form name="catalogForm">
+<div id="catalog" ></div>
+<br>
+<input type="button" onClick="addToCart()" value="Add to Cart">
+</form>
+<br>
+
+<h2>Your Shopping Cart</h2>
+<form name="shoppingCartForm">
+<div id="shoppingCart"></div>
+<br>
+<div id="total"></div>
+<br>
+<input type="button" onClick="checkoutCart()" value="Checkout">
+<input type="button" onClick="deleteCart()" value="Empty">
+<a href="shoppingCart/">(feed)</a>
+</form>
+</div>
+
+</body>
+</html>
diff --git a/sca-cpp/trunk/samples/store-vhost/htdocs/index.html b/sca-cpp/trunk/samples/store-vhost/htdocs/index.html new file mode 100644 index 0000000000..c27e54f753 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/htdocs/index.html @@ -0,0 +1,157 @@ +<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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>
+<title>Store</title>
+
+<script type="text/javascript" src="/js/tuscany-ref.js"></script>
+
+<script type="text/javascript">
+var component = new tuscany.sca.Component("Store");
+
+//@Reference
+var catalog = new tuscany.sca.Reference("catalog");
+
+//@Reference
+var shoppingCart = new tuscany.sca.Reference("shoppingCart");
+
+//@Reference
+var shoppingTotal = new tuscany.sca.Reference("shoppingTotal");
+
+var catalogItems;
+
+function catalog_getcatalogResponse(items, exception) {
+ if (exception){
+ alert(exception.message);
+ return;
+ }
+ var catalog = "";
+ for (var i=0; i<items.length; i++) {
+ var item = items[i].name + ' - ' + items[i].price;
+ catalog += '<input name="items" type="checkbox" value="' +
+ item + '">' + item + ' <br>';
+ }
+ document.getElementById('catalog').innerHTML=catalog;
+ catalogItems = items;
+
+}
+
+function shoppingCart_getResponse(feed) {
+ if (feed != null) {
+ var entries = feed.getElementsByTagName("entry");
+ var list = "";
+ for (var i=0; i<entries.length; i++) {
+ var content = entries[i].getElementsByTagName("content")[0];
+ var name = content.getElementsByTagName("name")[0].firstChild.nodeValue;
+ var price = content.getElementsByTagName("price")[0].firstChild.nodeValue;
+ list += name + ' - ' + price + ' <br>';
+ }
+ document.getElementById("shoppingCart").innerHTML = list;
+
+ shoppingTotal.apply("gettotal", shoppingTotal_gettotalResponse);
+ }
+}
+
+function shoppingTotal_gettotalResponse(total, exception) {
+ if (exception) {
+ alert(exception.message);
+ return;
+ }
+ document.getElementById('total').innerHTML = total;
+}
+
+function shoppingCart_postResponse(entry) {
+ shoppingCart.get("", shoppingCart_getResponse);
+}
+
+function addToCart() {
+ var items = document.catalogForm.items;
+ var j = 0;
+ for (var i=0; i<items.length; i++)
+ if (items[i].checked) {
+ var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><content type="application/xml">' +
+ '<item>' +
+ '<javaClass>' + catalogItems[i].javaClass + '</javaClass>' +
+ '<name>' + catalogItems[i].name + '</name>' +
+ '<currencyCode>' + catalogItems[i].currencyCode + '</currencyCode>' +
+ '<currencySymbol>' + catalogItems[i].currencySymbol + '</currencySymbol>' +
+ '<price>' + catalogItems[i].price + '</price>' +
+ '</item>' +
+ '</content></entry>';
+ shoppingCart.post(entry, shoppingCart_postResponse);
+ items[i].checked = false;
+ }
+}
+function checkoutCart() {
+ document.getElementById('store').innerHTML='<h2>' +
+ 'Thanks for Shopping With Us!</h2>'+
+ '<h2>Your Order</h2>'+
+ '<form name="orderForm">'+
+ document.getElementById('shoppingCart').innerHTML+
+ '<br>'+
+ document.getElementById('total').innerHTML+
+ '<br>'+
+ '<br>'+
+ '<input type="submit" value="Continue Shopping">'+
+ '</form>';
+ shoppingCart.del("", null);
+}
+function deleteCart() {
+ shoppingCart.del("", null);
+ document.getElementById('shoppingCart').innerHTML = "";
+ document.getElementById('total').innerHTML = "";
+}
+
+function init() {
+ try {
+ catalog.apply("getcatalog", catalog_getcatalogResponse);
+ shoppingCart.get("", shoppingCart_getResponse);
+ } catch(e){
+ alert(e);
+ }
+}
+</script>
+
+</head>
+
+<body onload="init()">
+<h1>Store</h1>
+<div id="store">
+<h2>Catalog</h2>
+<form name="catalogForm">
+<div id="catalog" ></div>
+<br>
+<input type="button" onClick="addToCart()" value="Add to Cart">
+</form>
+<br>
+
+<h2>Your Shopping Cart</h2>
+<form name="shoppingCartForm">
+<div id="shoppingCart"></div>
+<br>
+<div id="total"></div>
+<br>
+<input type="button" onClick="checkoutCart()" value="Checkout">
+<input type="button" onClick="deleteCart()" value="Empty">
+<a href="shoppingCart/">(feed)</a>
+</form>
+</div>
+
+</body>
+</html>
diff --git a/sca-cpp/trunk/samples/store-vhost/server-test b/sca-cpp/trunk/samples/store-vhost/server-test new file mode 100755 index 0000000000..26efd14c0e --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/server-test @@ -0,0 +1,43 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." +here=`readlink -f $0`; here=`dirname $here` +curl_prefix=`cat $here/../../modules/http/curl.prefix` + +# Setup +./start +sleep 2 + +# For this test to work, add the following line to your etc/hosts +127.0.0.1 jane.store.com joe.store.com + +# Test HTTP GET +$curl_prefix/bin/curl http://jane.store.com:8090/ 2>/dev/null >tmp/jane.html +diff tmp/jane.html htdocs/domains/jane/index.html +rc=$? + +# Cleanup +./stop +sleep 2 + +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/trunk/samples/store-vhost/shopping-cart.py b/sca-cpp/trunk/samples/store-vhost/shopping-cart.py new file mode 100644 index 0000000000..405adb85bf --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/shopping-cart.py @@ -0,0 +1,75 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Shopping cart implementation +import uuid +import sys + +cartId = "1234" + +# Get the shopping cart from the cache +# Return an empty cart if not found +def getcart(id, cache): + cart = cache("get", (id,)) + if cart is None: + return () + return cart + +# Post a new item to the cart, create a new cart if necessary +def post(collection, item, cache): + id = str(uuid.uuid1()) + cart = ((item[0], id, item[2]),) + getcart(cartId, cache) + cache("put", (cartId,), cart) + return (id,) + + +# Find an item in the cart +def find(id, cart): + if cart == (): + return ("Item", "0", ()) + elif id == cart[0][1]: + return cart[0] + else: + return find(id, cart[1:]) + +# Get items from the cart +def get(id, cache): + if id == (): + return ("Your Cart", cartId) + getcart(cartId, cache) + return find(id[0], getcart(cartId, cache)) + +# Delete items from the cart +def delete(id, cache): + if id == (): + return cache("delete", (cartId,)) + return True + +# Return the price of an item +def price(item): + return float(filter(lambda x: x[0] == "'price", item[2])[0][1]) + +# Sum the prices of a list of items +def sum(items): + if items == (): + return 0 + return price(items[0]) + sum(items[1:]) + +# Return the total price of the items in the cart +def gettotal(cache): + cart = getcart(cartId, cache) + return sum(cart) + diff --git a/sca-cpp/trunk/samples/store-vhost/ssl-start b/sca-cpp/trunk/samples/store-vhost/ssl-start new file mode 100755 index 0000000000..1e70d6edfb --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/ssl-start @@ -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. + +../../modules/http/httpd-ca-conf tmp localhost +../../modules/http/httpd-cert-conf tmp localhost +../../modules/http/httpd-conf tmp localhost 8090 htdocs +../../modules/http/httpd-ssl-conf tmp localhost 8453 htdocs +../../modules/server/server-conf tmp +../../modules/python/python-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +# Configure SCA Composite +SCAContribution `pwd`/ +SCAComposite store.composite + +EOF + +../../components/cache/memcached-start +../../modules/http/httpd-start tmp diff --git a/sca-cpp/trunk/samples/store-vhost/start b/sca-cpp/trunk/samples/store-vhost/start new file mode 100755 index 0000000000..6cbea136b3 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/start @@ -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. + +../../modules/http/httpd-conf tmp localhost 8090 htdocs vhost +../../modules/server/server-conf tmp +../../modules/python/python-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +# Configure SCA Composite for mass dynamic virtual Hosting +SCAVirtualContribution `pwd`/ +SCAVirtualComposite store.composite + +EOF + +../../components/cache/memcached-start +../../modules/http/httpd-start tmp diff --git a/sca-cpp/trunk/samples/store-vhost/stop b/sca-cpp/trunk/samples/store-vhost/stop new file mode 100755 index 0000000000..a59273b8ed --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/stop @@ -0,0 +1,21 @@ +#!/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. + +../../modules/http/httpd-stop tmp +../../components/cache/memcached-stop diff --git a/sca-cpp/trunk/samples/store-vhost/store.composite b/sca-cpp/trunk/samples/store-vhost/store.composite new file mode 100644 index 0000000000..045ebe6ec5 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/store.composite @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://store" + name="store"> + + <component name="Store"> + <t:implementation.python script="store.py"/> + <service name="Widget"> + <t:binding.http uri="store"/> + </service> + <reference name="catalog" target="Catalog"/> + <reference name="shoppingCart" target="ShoppingCart/Cart"/> + <reference name="shoppingTotal" target="ShoppingCart/Total"/> + </component> + + <component name="Catalog"> + <t:implementation.python script="fruits-catalog.py"/> + <property name="currencyCode">USD</property> + <service name="Catalog"> + <t:binding.jsonrpc uri="catalog"/> + </service> + <reference name="currencyConverter" target="CurrencyConverter"/> + </component> + + <component name="ShoppingCart"> + <t:implementation.python script="shopping-cart.py"/> + <service name="ShoppingCart"> + <t:binding.atom uri="shoppingCart"/> + </service> + <service name="Total"> + <t:binding.jsonrpc uri="total"/> + </service> + <reference name="cache" target="Cache"/> + </component> + + <component name="CurrencyConverter"> + <t:implementation.python script="currency-converter.py"/> + <service name="CurrencyConverter"> + <t:binding.jsonrpc uri="currencyConverter"/> + </service> + </component> + + <component name="Cache"> + <implementation.cpp path="../../components/cache" library="libmemcache"/> + <service name="Cache"> + <t:binding.atom uri="cache"/> + </service> + </component> + +</composite> diff --git a/sca-cpp/trunk/samples/store-vhost/store.py b/sca-cpp/trunk/samples/store-vhost/store.py new file mode 100644 index 0000000000..0b4e0f72fd --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/store.py @@ -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. + +# Store implementation + +def post(item, catalog, shoppingCart, shoppingTotal): + return shoppingCart("post", item) + +def getall(catalog, shoppingCart, shoppingTotal): + return shoppingCart("getall") + +def get(id, catalog, shoppingCart, shoppingTotal): + return shoppingCart("get", id) + +def getcatalog(catalog, shoppingCart, shoppingTotal): + return catalog("getcatalog") + +def gettotal(catalog, shoppingCart, shoppingTotal): + return shoppingCart("gettotal") + +def deleteall(catalog, shoppingCart, shoppingTotal): + return shoppingCart("deleteall") + +def delete(id, catalog, shoppingCart, shoppingTotal): + return shoppingCart("delete", id) + diff --git a/sca-cpp/trunk/samples/store-vhost/uec2-start b/sca-cpp/trunk/samples/store-vhost/uec2-start new file mode 100755 index 0000000000..2e59815637 --- /dev/null +++ b/sca-cpp/trunk/samples/store-vhost/uec2-start @@ -0,0 +1,46 @@ +#!/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. + +# Pass your EC2 public host name +if [ "$1" != "" ]; then + host=$1 +else + host="localhost" +fi + +# Ports 80, 443, 8090, 8453 need to be open +sudo ../../ubuntu/ip-redirect 80 8090 +sudo ../../ubuntu/ip-redirect 443 8453 + +../../modules/http/httpd-ca-conf tmp $host +../../modules/http/httpd-cert-conf tmp $host +../../modules/http/httpd-conf tmp $host 8090/80 htdocs +../../modules/http/httpd-ssl-conf tmp $host 8453/443 htdocs +../../modules/server/server-conf tmp +../../modules/python/python-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +# Configure SCA Composite +SCAContribution `pwd`/ +SCAComposite store.composite + +EOF + +../../components/cache/memcached-start +../../modules/http/httpd-start tmp + |