From e22bdc0f9572b6a1a2304799d481b25b3e962f87 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 11 Jan 2010 08:30:15 +0000 Subject: Improvements to autoconf build to make support for python, web service etc and relevant test cases optional and generate ac_defines used in ifdefs to check for debug and multithreading. Moved some optional code and test cases around to run them only when the tested features are built. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@897791 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/modules/server/mod-eval.hpp | 422 ++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 sca-cpp/trunk/modules/server/mod-eval.hpp (limited to 'sca-cpp/trunk/modules/server/mod-eval.hpp') diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp new file mode 100644 index 0000000000..4f95977fea --- /dev/null +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -0,0 +1,422 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 component implementations. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../scdl/scdl.hpp" +#include "../http/curl.hpp" +#include "../http/httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace server { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(server_rec* s) : s(s), home(""), wiringServerName("") { + } + + const server_rec* s; + string home; + string wiringServerName; +}; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf(char* dirspec) : dirspec(dirspec), contributionPath(""), compositeName("") { + } + const char* dirspec; + string contributionPath; + string compositeName; + list implementations; +}; + +/** + * Convert a result represented as a content + failure pair to a + * failable monad. + */ +const failable failableResult(const list& v) { + if (isNil(cdr(v))) + return car(v); + return mkfailure(string(cadr(v))); +} + +/** + * Handle an HTTP GET. + */ +const failable get(request_rec* r, const lambda&)>& impl) { + debug(r->uri, "modeval::get::uri"); + + // Inspect the query string + const list > args = httpd::queryArgs(r); + const list ia = assoc(value("id"), args); + const list 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 val = failableResult(impl(cons(func, httpd::queryParams(args)))); + if (!hasContent(val)) + return mkfailure(reason(val)); + + // Return JSON result + json::JSONContext cx; + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate an ATOM GET request and return an ATOM feed representing a collection of resources + const list path(httpd::pathValues(r->uri)); + if (isNil(cddr(path))) { + const failable val = failableResult(impl(cons("getall", list()))); + if (!hasContent(val)) + return mkfailure(reason(val)); + return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(content(val))), "application/atom+xml;type=feed", r); + } + + // Evaluate an ATOM GET and return an ATOM entry representing a resource + const failable val = failableResult(impl(cons("get", mklist(caddr(path))))); + if (!hasContent(val)) + return mkfailure(reason(val)); + return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(content(val))), "application/atom+xml;type=entry", r); +} + +/** + * Handle an HTTP POST. + */ +const failable post(request_rec* r, const lambda&)>& impl) { + const list ls = httpd::read(r); + debug(r->uri, "modeval::post::url"); + debug(ls, "modeval::post::input"); + + // 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")) { + json::JSONContext cx; + const list json = elementsToValues(content(json::readJSON(ls, cx))); + const list > 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 params = (list)cadr(assoc(value("params"), args)); + + // Evaluate the request expression + const failable val = failableResult(impl(cons(func, params))); + if (!hasContent(val)) + return mkfailure(reason(val)); + + // Return JSON result + return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r); + } + + // Evaluate an ATOM POST request and return the location of the corresponding created resource + if (contains(ct, "application/atom+xml")) { + + // Evaluate the request expression + const value entry = atom::entryValue(content(atom::readEntry(ls))); + const failable val = failableResult(impl(cons("post", mklist(entry)))); + if (!hasContent(val)) + return mkfailure(reason(val)); + + // Return the created resource location + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, httpd::url(content(val), r))); + r->status = HTTP_CREATED; + return OK; + } + + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Handle an HTTP PUT. + */ +const failable put(request_rec* r, const lambda&)>& impl) { + const list ls = httpd::read(r); + debug(r->uri, "modeval::put::url"); + debug(ls, "modeval::put::input"); + + // Evaluate an ATOM PUT request and update the corresponding resource + const list path(httpd::pathValues(r->uri)); + const value entry = atom::entryValue(content(atom::readEntry(ls))); + const failable val = failableResult(impl(cons("put", mklist(caddr(path), entry)))); + if (!hasContent(val)) + return mkfailure(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable del(request_rec* r, const lambda&)>& impl) { + debug(r->uri, "modeval::delete::url"); + + // Evaluate an ATOM delete request + const list path(httpd::pathValues(r->uri)); + if (isNil(cddr(path))) { + + // Delete a collection of resources + const failable val = failableResult(impl(cons("deleteall", list()))); + if (!hasContent(val)) + return mkfailure(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; + } + + // Delete a resource + const failable val = failableResult(impl(cons("delete", mklist(caddr(path))))); + if (!hasContent(val)) + return mkfailure(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Translate a component request. + */ +int translate(request_rec *r) { + gc_scoped_pool pool(r->pool); + if (strncmp(r->uri, "/components/", 12) != 0) + return DECLINED; + r->handler = "mod_tuscany_eval"; + return OK; +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + gc_scoped_pool pool(r->pool); + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + httpdDebugRequest(r, "modeval::handler::input"); + + // Set up the read policy + const int rc = httpd::setupReadPolicy(r); + if(rc != OK) + return rc; + + // Get the component implementation lambda + DirConf& dc = httpd::dirConf(r, &mod_tuscany_eval); + const list path(httpd::pathValues(r->uri)); + const list impl(assoctree(cadr(path), dc.implementations)); + if (isNil(impl)) + return HTTP_NOT_FOUND; + + // Handle HTTP method + const lambda&)> l(cadr(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 mkproxy(const value& ref, const string& base) { + return lambda&)>(http::proxy(base + string(scdl::name(ref)))); +} + +const list proxies(const list& refs, const string& base) { + if (isNil(refs)) + return refs; + return cons(mkproxy(car(refs), base), proxies(cdr(refs), base)); +} + +extern const failable&)> > readImplementation(const string& itype, const string& path, const list& px); + +const value confImplementation(DirConf& dc, ServerConf& sc, server_rec& server, const value& comp) { + const value impl = scdl::implementation(comp); + const string path = dc.contributionPath + string(scdl::uri(impl)); + + // Convert component references to configured proxy lambdas + ostringstream base; + if (sc.wiringServerName == "") + base << (server.server_scheme == NULL? "http" : server.server_scheme) + << "://" << (server.server_hostname == NULL? "localhost" : server.server_hostname) + << ":" << (server.port == 0? 80 : server.port) + << "/references/" << string(scdl::name(comp)) << "/"; + else + base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/"; + const list px(proxies(scdl::references(comp), str(base))); + + // Read and configure the implementation + const failable&)> > cimpl(readImplementation(elementName(impl), path, px)); + if (!hasContent(cimpl)) + return reason(cimpl); + return content(cimpl); +} + +/** + * Return a tree of component-name + configured-implementation pairs. + */ +const list componentToImplementationAssoc(DirConf& dc, ServerConf& sc, server_rec& server, const list& c) { + if (isNil(c)) + return c; + return cons(mklist(scdl::name(car(c)), confImplementation(dc, sc, server, car(c))), componentToImplementationAssoc(dc, sc, server, cdr(c))); +} + +const list componentToImplementationTree(DirConf& dc, ServerConf& sc, server_rec& server, const list& c) { + return mkbtree(sort(componentToImplementationAssoc(dc, sc, server, c))); +} + +/** + * Read the components declared in a composite. + */ +const failable > readComponents(const string& path) { + ifstream is(path); + if (fail(is)) + return mkfailure >(string("Could not read composite: ") + path); + return scdl::components(readXML(streamList(is))); +} + +/** + * Configure the components declared in the deployed composite. + */ +const bool confComponents(DirConf& dc, ServerConf& sc, server_rec& server) { + if (dc.contributionPath == "" || dc.compositeName == "") + return true; + const failable > comps = readComponents(dc.contributionPath + dc.compositeName); + if (!hasContent(comps)) + return true; + dc.implementations = componentToImplementationTree(dc, sc, server, content(comps)); + debug(dc.implementations, "modeval::confComponents::implementations"); + return true; +} + +/** + * Configuration commands. + */ +const char *confHome(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf(cmd, &mod_tuscany_eval); + sc.home = arg; + return NULL; +} +const char *confWiringServerName(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf(cmd, &mod_tuscany_eval); + sc.wiringServerName = arg; + return NULL; +} +const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf(cmd, &mod_tuscany_eval); + DirConf& dc = *(DirConf*)c; + dc.contributionPath = arg; + confComponents(dc, sc, *cmd->server); + return NULL; +} +const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf(cmd, &mod_tuscany_eval); + DirConf& dc = *(DirConf*)c; + dc.compositeName = arg; + confComponents(dc, sc, *cmd->server); + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"), + {NULL, NULL, NULL, 0, NO_ARGS, NULL} +}; + +int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, unused server_rec *s) { + return OK; +} + +void childInit(apr_pool_t* p, server_rec* svr_rec) { + gc_scoped_pool pool(p); + ServerConf* c = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_eval); + if(c == NULL) { + cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } +} + +void registerHooks(unused apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_eval = { + STANDARD20_MODULE_STUFF, + // dir config and merger + tuscany::httpd::makeDirConf, NULL, + // server config and merger + tuscany::httpd::makeServerConf, NULL, + // commands and hooks + tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks +}; + +} -- cgit v1.2.3