pax_global_header00006660000000000000000000000064134647667520014536gustar00rootroot0000000000000052 comment=fa17e2a675cf6ff5b60005bc14d89c62a217a2f5 libzeep-3.0.5/000077500000000000000000000000001346476675200131755ustar00rootroot00000000000000libzeep-3.0.5/.gitignore000066400000000000000000000001411346476675200151610ustar00rootroot00000000000000libzeep_Data/ obj .vscode/ipch *.a *.so* compile_commands.json doc/bin debian/ .pc libzeep-*.tgz libzeep-3.0.5/.travis.yml000066400000000000000000000006471346476675200153150ustar00rootroot00000000000000dist: xenial before_install: - sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu xenial universe" - sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu xenial main" - sudo apt-get install debian-keyring debian-archive-keyring - sudo apt-key update - sudo apt-get update - sudo apt-get install libboost-all-dev install: true language: cpp compiler: clang script: make && make -C tests libzeep-3.0.5/LICENSE_1_0.txt000066400000000000000000000024721346476675200154640ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libzeep-3.0.5/README.md000066400000000000000000000210001346476675200144450ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/mhekkel/libzeep.svg?branch=master)](https://travis-ci.org/mhekkel/libzeep) About libzeep ============= Libzeep was developed to make it easy to create SOAP servers. And since working with SOAP means working with XML and no decent C++ XML library existed on my radar I've created a full XML library as well. The XML part of libzeep consists of a validating parser, a DOM(-like) node implementation, an XPath search engine and a XML writer/formatter. The validation works based on DOCTYPE definitions, XML schema support will be added in a later release. The performance of the parser is not optimal yet although it performs very decently. If speed is critical and you really need that few percent saving you can choose to use expat as a parser instead. Please note that libzeep aims to provide a fully compliant XML processor as specified by the W3 organisation (see: http://www.w3.org/TR/xml ). This means it is as strict as the standard requires and it stops processing a file when a validation of the well-formedness is encountered, or when a document appears to be invalid when it is in validating mode. Error reporting is done in this case, although I admit that error reporting should be improved. The SOAP server part of libzeep makes it very easy to create a SOAP server software in C++. You use it to export a C++ object's methods as SOAP actions. The library generates a WSDL on-the-fly for the exported actions and it also has a REST style interface. libzeep requires the Boost libraries and currently requires at least version 1.36 of Boost since it uses the new asio library for network I/O. The current version of libzeep has been tested with boost 1.55 and newer only. To use libzeep, you have to edit the makefile and make sure the paths to your installation of boost libraries are correct. After this you simply type `make zeep-test` and a `zeep-test` executable is build. You can also cd into the tests directory and build the two test applications called xpath-test and parser-test. For Windows users there's a VC solution file in the msvc directory. Full documentation for libzeep in docbook format can be found at [www.hekkelman.com/libzeep-doc](http://www.hekkelman.com/libzeep-doc/) ## XML Library -- usage Using the XML library of libzeep is fairly trivial. The first class you use is the `zeep::xml::document` class. You can use this class to read XML files and write them out again. Reading and writing is strictly done using stl iostreams. Make sure you open these streams in binary mode, random parsing errors will occur if you don't when running in Windows. #include #include "zeep/xml/document.hpp" using namespace std; using namespace zeep; ... xml::document doc; doc.set_validating(true); // validation is off by default ifstream file("/...", ios::binary); // avoid CRLF translation file >> doc; Now that you have a document, you can walk its content which is organised in nodes. There are several nodes classes, the most interesting for most is `xml::element`. These elements can have children, some of which are also elements. Internally the nodes are stored as linked lists. However, to conform to STL coding practices, `xml::element` can used like a container. The iterator of `xml::element` (which it inherits from its base class `xml::container`) only returns child `xml::element` objects skipping over comments and processing instructions. So, to iterate over all elements directly under the first element of a document, we do something like this: xml::element& first = *doc.child(); for (xml::document::iterator e = first.begin(); e != first.end(); ++e) cout << e.name() << endl; Likewise you can iterate over the attributes of an `xml::element`, like this: for (xml::element::attribute_iterator a = e.attr_begin(); a != e.attr_end(); ++a) cout << a->name() << endl; More often you're interested in a specific element among many others. Now you can recursively iterate the tree until you've found what you're looking for, but it is way easier to use xpaths in that case. Let say you need the element `book` having an attribute `title` with value *Du côté de chez Swann*, you could do this: xml::element* book = doc.find("//book[@title='Du côté de chez Swann']"); You can access the attributes by name: assert(book->get_attribute("author") == "Proust"); And the content, contained in the text nodes of an element: cout << book->content() << endl; And writing out an XML file again can be done by writing an `xml::document`: cout << doc; Or by using `xml::writer` directly. libzeep has XML Namespace support. The qname method of the nodes returns a qualified name, that is the namespace prefix, a colon and the localname contatenated. (Something like `ns:book`). The method name() returns the qname() with its prefix stripped off. ## SOAP Server -- usage Have a look at the zeep-test.cpp file to see how to create a server. This example server is not entirely trivial since it has three exported methods that each take another set of parameters. When you run this sample server, it listens to port 10333 on your localhost. You can access the wsdl by pointing your browser at: http://localhost:10333/wsdl and to access e.g. the Count method of this server from the REST interface you can browse to: http://localhost:10333/rest/Count/db/sprot/booleanquery/os:human As you can see, parameters and values are passed in the URL, order is not important and multiple id/value pairs can be specified for input parameters that allow more than one value. The steps to create a server are: Create a new server object that derives from `soap::server`. The constructor of this object should call the inherited constructor passing it the namespace and the service name for this new SOAP server as well as the internet address and port to listen to. Inside the constructor of your new server object you have to register the methods of the server you want to export. These methods can take any number of input arguments and only one output parameter which is the last parameter of the method. The result of these methods should be void. Please note that if the method's last (output) parameter is a struct, then the fields of this struct will be moved up in the Response message of the action in the WSDL. To the outside world this method will look like it has multiple output parameters. This was done to be compatible with another popular SOAP tool but the result may be a bit confusing at times. To register the methods you have to call the inherited `register_action` method which takes four parameters: - the name of the action as it is published - the pointer for your server object, usually it is `this`. - a pointer to the method of your server object you want to export - an array of pointers to the exported names for each of the parameters of the exported method/action. The size of this array should be exactly as long as the arity of your method. You will get a compilation error if it isn't. If you export enum parameters, you add the names for all possible values of your enumerated type by using the SOAP_XML_ADD_ENUM macro. The first parameter should be the name of the enum type, the second the value identifier. If you have structured types you want to export, you have to do two things. First of all the structure needs to be able to serialize itself. You do this by adding the method serialize to each struct. For a struct consisting of two fields, db and id, you specify: template void serialize(Archive& ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(db) & BOOST_SERIALIZATION_NVP(id); } The next thing you need for each struct is to set its exported name using the SOAP_XML_SET_STRUCT_NAME macro. And that's it. The moment the constructor is done, your server is ready to run. You can start it by calling the `run` method, normally you do this from a new thread. The servers will start listening to the address and port you specified. Beware though that the server is multithreaded and so your exported methods should be reentrant. The number of threads the server will use can be specified in the constructor of the `soap::server` base class. If your server is behind a reverse proxy, you set the actual location in the WSDL from which it is accessible by calling the server's `set_location` method. Inside your server method you have access to the ostream object used to write out log entries by using the inherited log() member function. That's it. This is a first release, please send all the problems and/or bugs you encounter to: maarten@hekkelman.com -maarten hekkelman libzeep-3.0.5/changelog000066400000000000000000000065601346476675200150560ustar00rootroot00000000000000Version 3.0.2 - Change in zeep/xml/serialize.hpp for gcc 4.7 compiler Version 3.0.1 - added cast to uint32 in webapp-el to allow compilation on s390 Version 3.0 - Support for non-intrusive serialization. The call to serialize is now done by the templated struct zeep::xml::struct_serializer. You can create a specialization for this struct to do something else than calling MyClass::serialize. - xml::document now has serialize and deserialize members. - A streaming input added, process_document_elements calls the callback for all elements that match a given xpath. - ISO8859-1 support (finally) - some xpath additions (matches e.g.) - changed signature of various find routines to work with const char* - changed authentication mechanism in webapp to allow multiple realms - some small changes in writing out XML documents/xml::writer - added line number to validation error messages - process value tag of mrs:option tag - el processing returns original string if it does not contain an expression - in expression language, support var1[var2] constructs - fix in writing doctype declaration - insert/erase implementations of zeep::xml::node... - fixed bug in el implementation (dividing numbers) - extended log format of HTTP server to allow better awstat logs (using the extra fields for SOAP calls). Also writes the X-Forwarded-For client if any. - Patches provided by Patrick Rotsaert: serializer for xsd:time and optional data types based on boost::optional. - Split out log_request as a virtual method in http::server - Added quick and dirty test for requests from mobile clients - Added virtual destructors to all base classes. - OPTIONS and HEAD support in web server Version 2.9.0 - Added some calls to xml::writer to write e.g. xml-decl and doctypes Version 2.8.2 - Fix in unicode support code - Preliminary support for handling authentication Version 2.8.1 - removed boost::ptr_vector/ptr_list. - work around a crashing bug in el::object[string] when compiling with icpc Version 2.8.0 - write_content added. - nullptr instead of nil, added a stub for old compilers. - fix in el::object (mixing up uint64 and size_t) Version 2.6.3 - Fix for stack overflow in delete large XML documents Version 2.6.2 - Apparently the word size has changed on amd64/GNUC targets. I've switched to a more robust template selection algorithm for WSDL generation. Version 2.6.1 - Fix in keep-alive (clear reply object after each served reply) - Implemented missing at() virtual method for el::vector - Writing comments now validates output - check mounted paths instead of only the root for handlers - optimization flags in makefile Version 2.6.0 - Changed parameter_map (for webapp) into a multimap Version 2.5.2 - Throw exception when attempting to write null character. Version 2.5.1 - Removed the use of split_iterator from webapp since it generated crashes when built as a shared library... Version 2.5.0 - added webapp, a base class used to create web applications, it uses XHTML templates to fill in. It uses a script language to enable interaction with the C++ code. Version 2.1.0 - support for HTTP/1.1 - added multiplication in xpath expression language... oops - revised interface for container::iterator, now it is possible to use more STL and boost functions on a container directly, like: xml::container cnt = ...; foreach (node* n, cnt) { cout << n->name() << endl; } libzeep-3.0.5/count-req.xml000066400000000000000000000007401346476675200156350ustar00rootroot00000000000000 sprot bla bla libzeep-3.0.5/doc/000077500000000000000000000000001346476675200137425ustar00rootroot00000000000000libzeep-3.0.5/doc/Jamfile.v2000066400000000000000000000020561346476675200155650ustar00rootroot00000000000000using xsltproc ; using boostbook : /usr/share/xml/docbook/stylesheet/nwalsh : /usr/share/xml/docbook/schema/dtd/4.2 ; using doxygen ; using quickbook ; doxygen autodoc : [ glob ../zeep/*.hpp ] [ glob ../zeep/xml/*.hpp ] [ glob ../zeep/http/*.hpp ] [ glob ../zeep/http/webapp/*.hpp ] : EXTRACT_ALL=YES "PREDEFINED=\"LIBZEEP_DOXYGEN_INVOKED\" \\ \"BOOST_DEDUCED_TYPENAME=typename\" \\ \"BOOST_STATIC_CONSTANT(x,y)=static x const y\" \\ \"BOOST_XPR_NONDEDUCED_TYPE_(X)=X\"" HIDE_UNDOC_MEMBERS=NO EXTRACT_PRIVATE=NO ENABLE_PREPROCESSING=YES MACRO_EXPANSION=YES EXPAND_ONLY_PREDEF=YES SEARCH_INCLUDES=YES INCLUDE_PATH=$(BOOST_ROOT) ; boostbook libzeep-doc : libzeep-doc.qbk : autodoc ; libzeep-3.0.5/doc/Jamroot.jam000066400000000000000000000000011346476675200160350ustar00rootroot00000000000000 libzeep-3.0.5/doc/libzeep-doc.qbk000066400000000000000000000613151346476675200166440ustar00rootroot00000000000000[article libzeep [quickbook 1.5] [version 3.0] [copyright 2012-2019 Maarten L. Hekkelman] [license Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at [@http://www.boost.org/LICENSE_1_0.txt]) ] [authors [Hekkelman, Maarten L.]] ] [def __node__ [classref zeep::xml::node `zeep::xml::node`]] [def __element__ [classref zeep::xml::element `zeep::xml::element`]] [def __attribute__ [classref zeep::xml::attribute `zeep::xml::attribute`]] [def __container__ [classref zeep::xml::container `zeep::xml::container`]] [def __document__ [classref zeep::xml::document `zeep::xml::document`]] [def __serializer__ [classref zeep::xml::serializer `zeep::xml::serializer`]] [def __deserializer__ [classref zeep::xml::deserializer `zeep::xml::deserializer`]] [def __http_server__ [classref zeep::http::server `zeep::http::server`]] [def __http_request__ [classref zeep::http::request `zeep::http::request`]] [def __http_reply__ [classref zeep::http::reply `zeep::http::reply`]] [def __soap_server__ [classref zeep::server `zeep::server`]] [def __webapp__ [classref zeep::http::webapp `zeep::http::webapp`]] [def __add_processor__ [memberref zeep::http::basic_webapp::add_processor `zeep::http::basic_webapp::add_processor`]] [def __el_scope__ [classref zeep::http::el::scope `zeep::http::el::scope`]] [def __el_object__ [classref zeep::http::el::object `zeep::http::el::object`]] [section:overview Overview] Libzeep is packaged as one library, but actually contains two different libraries. The first part of libzeep consists of code to read, manipulate and write XML. It contains a validating XML parser, an XPath implementation to query a DOM tree, code to serialize objects into and out of XML and finally it contains an XML writing module. The second part of libzeep is targeted to writing SOAP and REST servers as well as full web applications using C++. There is a simple HTTP server implementation, code to create SOAP (and REST) servers out of existing C++ objects and there is code to create complete web applications that work a bit like popular Java web application frameworks. The libzeep web application framework turns page templates consisting of XHTML with custom tags and a custom script language into HTML. [endsect] [section:intro Introduction] [section:xml XML Parser] Libzeep comes with a validating XML parser. Using this parser is as simple as writing: #include #include int main() { std::ifstream file("test.xml"); __document__ doc(file); ... } This will parse the file =text.xml= and create an object `doc` containing the DOM tree. To traverse this tree you can use the various member functions of doc which derives from the generic __container__ class. Siblings in the DOM tree are stored as linked lists and some elements can have children. To make life easier, you can iterate over elements using STL iterators. Suppose our =test.xml= file contains the following XML:[teletype] John Doe Jane Jones You could print out the file like this:[c++] // a document contains at most one child node __element__* persons = doc.child(); // begin/end will return iterators to elements for (__container__::iterator person = persons->begin(); person != persons->end(); ++person) { for (__container__::iterator name = (*person)->begin(); name != (*person)->end(); ++name) std::cout << (*name)->name() << " = " << (*name)->content() << std::endl; } Of course, using the new for loop construct, this code would be much more readable: for (auto person : *persons) { for (auto name : *person) std::cout << name->name() << " = " << name->content() << std::endl; } But if your compiler does not support that syntax, you can always use `boost::range` instead: BOOST_FOREACH (__element__* person, *persons) { BOOST_FOREACH (__element__* name, *person) std::cout << name->name() << " = " << name->content() << std::endl; } Accessing attributes is done using the member function `element::get_attribute()`. [endsect] [section:xml_serialization XML Serialization] An alternative way to read/write XML files is using serialization. To do this, we first construct a structure called Person. We add a templated function to this struct just like in `boost::serialize` and then we can read the file. #include #include #include #include struct Person { std::string firstname; std::string lastname; template void serialize(Archive& ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(firstname) & BOOST_SERIALIZATION_NVP(lastname); } }; int main() { std::ifstream file("test.xml"); __document__ doc(file); __deserializer__ ds(doc.child()); std::vector person; // the variable name person must be the same as the name of the XML element person ds & BOOST_SERIALIZATION_NVP(person); // alternative is to use which allows another variable name: // ds & boost::serialization::make_nvp("person", person); } Since libzeep 3.0 we can reduce the code required. int main() { std::ifstream file("test.xml"); __document__ doc(file); std::vector persons; // New way of deserializing persons doc.deserialize("persons", persons); } And to write out the persons, we do something similar. __document__ doc; doc.serialize("persons", persons); std::ofstream file("test-out.xml"); file << doc; To find out more about serialization, look at the reference for __serializer__ [endsect] [section:xpath XPath 1.0] Libzeep comes with a [@http://www.w3.org/TR/xpath/ XPath 1.0] implementation. You can use this to locate elements in a DOM tree easily. For a complete description of the XPath specification you should read the documentation at e.g. [@http://www.w3.org/TR/xpath/] or [@http://www.w3schools.com/xpath/default.asp]. The way it works in libzeep is that you can call `find()` on an __element__ object and it will return a zeep::xml::element_set object which is actually a `std::list` of __element__ pointers of the elements that conform to the specification in XPath passed as parameter to `find()`. An alternative method `find_first()` can be used to return only the first element. An example where we look for the first person in our test file with the lastname Jones: __element__* jones = doc.child()->find_first("//person[lastname='Jones']"); [endsect] [section:http HTTP Server] Creating a HTTP server with libzeep is as simple as: #include class my_server : public __http_server__ { virtual void handle_request(const __http_request__& req, __http_reply__& rep) { ... // do something useful } }; int main() { my_server server; server.bind("0.0.0.0", 80); server.run(1); } Of course you will have to fill in the `handle_request` part... [endsect] [section:soap SOAP Server] Setting up a SOAP server is very easy. Let's continue with our test file and serve it as a SOAP/REST server. We already created the Person struct. The most simple server we can create is one that lists all persons in the test file: #include #include using namespace std; ... // define the Person struct as above class my_server : public __soap_server__ { public: my_server(); // The method we want to export void ListPersons(vector& result); }; void my_server::ListPersons(vector& result) { std::ifstream file("test.xml"); __document__ doc(file); __deserializer__ ds(doc.child()); ds & boost::serialization::make_nvp("person", result); } my_server::my_server() : __soap_server__("http://www.example.org/soaptest", "soaptest") { // assign a name to the Person struct (will appear in the WSDL e.g.) zeep::xml::serialize_struct::set_struct_name("person"); // assign names to the parameters of the exported method, in this case there's only // one return value to name const char* kListPersonsParameterNames[] = { "response" }; register_action("ListPersons", this, &my_server::ListPersons, kListPersonsParameterNames); } int main() { my_server server; server.bind("192.168.0.1", 8083); server.run(1); // keep our server single threaded } After building this server and running it, you can access the REST version of this routine at [@http://192.168.0.1:8083/rest/ListPersons] and there's a WSDL at [@http://192.168.0.1:8083/wsdl] [endsect] [endsect] [section:soap Creating a SOAP and REST server] [section:intro Introduction] SOAP and REST are two ways to export functionality over the web. Both have their strongness and weakness. SOAP enforces a strict type checking on input and output parameters. It works with a formal description file called WSDL that specifies all the exposed functionality and how to invoke this. Many tools exist that can read WSDL files and create client code that uses the exposed functions. REST on the other hand, is much easier to use ad hoc. It passes the arguments to the invoked functions in the URL and uses standard GET, POST and PUT methods of the HTTP protocol. libzeep is mainly focussed to using SOAP, but allows to access the exported functionality in a REST like way. Not all functions can be accessed this way, if the input parameters are some complex type, you're out of luck. This documentation will focus on SOAP, but if the function is simple, you can test it using REST from a browser. [endsect] [section:basics A real world example] Creating a SOAP server using libzeep is very easy. The bulk of the work is done by libzeep, you only have to specify what methods to expose and optionally what datatypes. To demonstrate this, we will create a simple SOAP server that allows the client to search for documents in a databank. Lets start with the initial code, the declaration of our server object. #include using namespace std; class MyServer : public __soap_server__ { public: struct MyHit { long id; float score; string title; template void serialize(Archive& ar, const unsigned int version) { ar & BOOST_SERIALIZATION_NVP(id) & BOOST_SERIALIZATION_NVP(score) & BOOST_SERIALIZATION_NVP(title); } }; enum MyAlgorithm { algVector, algDice, algJaccard }; MyServer(); void CountDocuments(long& outCount); void GetDocument(long inID, string& outDocument); void FindDocument(const vector& inTerms, MyAlgorithm inAlgorithm, vector& outResponse); }; Nothing special so far. Apart from inheriting from __soap_server__, this code could have been code you already had lying around. The addition of the `serialize` method to `MyHit` may also have been new to the code. The implementation of the actual server methods are also straightforward: void MyServer::CountDocuments(long& outCount) { long count = 1; // real code should return something more sensible of course outCount = count; } void MyServer::GetDocument(long inID, string& outDocument) { if (inID == 1) outDocument = "The first document!"; else throw zeep::exception("document %ld not found", inID); } void MyServer::FindDocument(const vector& inTerms, MyAlgorithm inAlgorithm, vector& outResponse) { if (inTerms.size() == 1 and inAlgorithm == algVector) { MyHit hit = { 1, 1.0f, "The first hit" }; outResponse.push_back(hit); } } Not very useful code, but it gives you an idea how simple it is to create a server. You don't have to do anything special, it's still code you could have written for some other purpose. Note that the GetDocument method throws an exception. The result in a SOAP server will be a SOAP Fault being returned containing the text 'document x not found'. Unfortunately, this is not all that needs to be done, we still have to tell libzeep what methods and what datatypes to expose. That's what we do in the constructor for `MyServer`. MyServer::MyServer() : __soap_server__("http://www.example.org/MyServer", "searchMyServer") { // first export the data types, start with MyHit zeep::xml::serialize_struct::set_struct_name("hit"); // and then the MyAlgorithm enum zeep::xml::enum_map::instance("algorithm").add_enum() ("vector", algVector) ("dice", algDice) ("jaccard", algJaccard); // Now export the methods, start with CountDocuments const char* kCountDocumentsParamNames[] = { "response" }; register_action("CountDocuments", this, &MyServer::CountDocuments, kCountDocumentsParamNames); // then GetDocument const char* kGetDocumentParamNames[] = { "id", "response" }; register_action("GetDocument", this, &MyServer::GetDocument, kGetDocumentParamNames); const char* kFindDocumentParamNames[] = { "terms", "algorithm", "response" }; register_action("FindDocument", this, &MyServer::FindDocument, kFindDocumentParamNames); } We start the constructor by calling the constructor of our base class, __soap_server__. We then continue by exporting the data types. Our MyHit datatype is exported under the name 'hit' and MyAlgorithm is exported as 'algorithm'. The various values of MyAlgorithm are exported under a new name as well. After exporting the datatypes, we export the methods. We do this by calling `register_action` specifying the parameters for the exported method name, the callback to make and the names for the parameters. And that's all. All that's left is to write a `main`. int main() { MyServer server; server.bind("0.0.0.0", 80); server.run(1); } And that will run our code in a single threaded server. If you run this code on your local machine you can test the REST versions of the code by visiting the following URL's with a web browser: [@http://localhost/rest/CountDocuments] Will return a SOAP envelope containing *1* in a response element [@http://localhost/rest/GetDocument/id/1] [@http://localhost/rest/GetDocument/id/2] Will return a SOAP Fault [@http://localhost/rest/FindDocument/terms/bla/algorithm/vector] [endsect] [endsect] [section:webapp Creating a Web Application] [section:intro Introduction] This section will guide you through all the code you have to write to create an interactive web application using libzeep. The way this works in libzeep looks a lot like popular frameworks found for Java. If you're familiar with JSP and e.g. Struts, you'll notice the similarities. [endsect] [section:basics The building blocks] It is very inconvenient to write HTML code in C++ directly using string concatenations and streams. Therefore, a separation has been made. All HTML is put into XHTML template files. These template files can use special tags to generate HTML tags based on data provided by the server application. A script language that looks a lot like JSP 'Expression Language' (or =el= in short) is used to program conditional constructs. Communication between this =el= script and the server application is done via =el::object= data objects. [endsect] [section:hello Hello world!] Let's start with a simple hello world example. We first create a template file, save this file as =hello.xhtml=.[teletype] Hello

Hello, world!

This is a very simple, strict XHTML 1.1 file. We will serve it with our server:[c++] #include #include using namespace std; class MyWebApp : public __webapp__ { public: MyWebApp(); void handle_welcome(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply); }; MyWebApp::MyWebApp() { mount("", boost::bind(&MyWebApp::handle_welcome, this, _1, _2, _3)); } void MyWebApp::handle_welcome(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply) { create_reply_from_template("hello.xhtml", scope, reply); } int main() { MyWebApp server; server.bind("0.0.0.0", 80); server.run(1); } By calling `mount` with the empty string, we tell libzeep to redirect all access to the base URL to handle_welcome. This means that visiting the URL [@http://localhost/] should now return a page containing the string 'Hello, world!'. [endsect] [section:forms Using forms] Now lets create a form to pass some data from the browser to the server and back. Save the following file as =form.xhtml=.[teletype] Form example

Hello ${name}!

Please enter your name and press the Submit button

We add the zeep prefix to our html tag, it has the value ="http://www.cmbi.ru.nl/libzeep/ml"= which is the same as the default value for the `ns` parameter in the __webapp__ constructor. Note that the input tag has an attribute with value '${data}'. This is a piece of expression language script. This will be explained below:[c++] #include #include #include using namespace std; class MyWebApp : public __webapp__ { public: MyWebApp(); void handle_welcome(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply); void handle_salute(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply); }; MyWebApp::MyWebApp() { mount("", boost::bind(&MyWebApp::handle_welcome, this, _1, _2, _3)); mount("salute", boost::bind(&MyWebApp::handle_salute, this, _1, _2, _3)); } void MyWebApp::handle_welcome(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply) { create_reply_from_template("form.xhtml", scope, reply); } void MyWebApp::handle_salute(const __http_request__& request, const __el_scope__& scope, __http_reply__& reply) { zeep::http::parameter_map params; get_parameters(scope, params); string name = params.get("name", "").as(); __el_scope__ sub(scope); sub.put("name", name); create_reply_from_template("form.xhtml", sub, reply); } int main() { MyWebApp server; server.bind("0.0.0.0", 80); server.run(1); } This time, we add a new handler for the 'salute' page. The form has an action that points to this salute page. As can be seen in the `handle_salute` method, we first collect the parameters passed in. Parameters are accessed by name. We then create a sub scope of the scope passed in. In this sub scope we put the value of the parameter so that the XHTML processor can access it. And then we return a reply based on the contents of the =form.xhtml= template and the contents of the sub scope we created. [endsect] [section:el Using `el` script] `el` means /Expression Language/. It is a script language that tries to be like [@http://en.wikipedia.org/wiki/Unified_Expression_Language]. The objects can be created in the C++ server code using the __el_object__ class. Object created this way are then stored in an __el_scope__ object and passed along to the XHTML processing code. __el_object__ objects can contain simple data and arrays. E.g., to create an array you could write: using namespace zeep::http; vector ints; for (int i = 0; i < 10; ++i) { el::object int_object; int_object["value"] = i; ints.push_back(int_object); } scope.put("ints", el::object(ints)); And then you can access this in the XHTML:[teletype] 1: ${ints[1].value}, 2: ${ints[2].value} [c++]Which should output "1: 1, 2: 2" [endsect] [section:processing_tags Processing Tags] The method `create_reply_from_template` takes the name of a template file and a scope to generate the output XHTML. We've already seen that `el` scripts are processed by this method. But there is more, several special tags in the template are processed in a special way. These tags are in a separate XML namespace. You can change this name space using the `ns` parameter in the __webapp__ constructor, the default is =http://www.cmbi.ru.nl/libzeep/ml=. In the template for the form example above you might have noticed the == tag. This tag takes one attribute called `test` and the value of this tag is interpreted as a `el` script. If the script evaluates to something other than empty, zero or false, the content of the == tag is included, otherwise it is discarded in the output. There are several predefined processing tags which are summarized below. You can also add your own processing tags using the __add_processor__ method. This method takes a `std::string` parameter for the name of the tag and a `processor_type` parameter which is a call back function. [table List of predefined processing tags [[tag name (without prefix)][Description][Example]] [ [include] [Takes one parameter named `file` and replaces the tag with the processed content of this file] [ [^] ] ] [ [if] [Takes one parameter named `test` containing an `el` script. This script is evaluated and if the result is not empty, zero or false, the content of the `if` tags is inserted in the output. Otherwise, the content is discarded.] [[teletype] `` Hello ${name} `` ] ] [ [iterate] [Takes two parameters, `collection` which contains an `el` script that evaluates to an array `el::object` and a name in `var`. The content of the `iterate` tag is included for each value of `collection` and `var` will contain the current value.] [ ``
  • ${name}
`` ] ] [ [for] [Takes three parameters. The parameters `begin` and `end` should evaluate to a number. The parameter `var` contains a name that will be used to hold the current value when inserting the content of the `for` tag in each iteration of the for loop between `begin` and `end`.] [ `` ${i}, `` ] ] [ [number] [Format the number in the `n` parameter using the `f` format. This is limited to the formats ='#.##0'= and ='#.##0B'= for now. The first formats an integer value using thousand separators, the second tries to format the integer value in a power of two multiplier (kibi, mebi, etc.) with a suffix of `B`, `M`, `G`, etc.] [ `` Will output 1K `` ] ] [ [options] [This tag will insert multiple =