summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2016-08-21 13:06:27 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2016-08-21 13:06:27 +0100
commitb388a5537f2e3b459021118fcda18e2a58da3492 (patch)
tree52f8beda2b103d5411b63cef900060a7433708ae
parentAllow inclusion of user headers (diff)
downloadicespider-b388a5537f2e3b459021118fcda18e2a58da3492.tar.bz2
icespider-b388a5537f2e3b459021118fcda18e2a58da3492.tar.xz
icespider-b388a5537f2e3b459021118fcda18e2a58da3492.zip
Add support for custom output serializers
-rw-r--r--icespider/common/routes.ice9
-rw-r--r--icespider/compile/routeCompiler.cpp34
-rw-r--r--icespider/compile/routeCompiler.h2
-rw-r--r--icespider/core/core.cpp1
-rw-r--r--icespider/core/irouteHandler.cpp25
-rw-r--r--icespider/core/irouteHandler.h5
-rw-r--r--icespider/unittests/Jamfile.jam7
-rw-r--r--icespider/unittests/testApp.cpp24
-rw-r--r--icespider/unittests/testCompile.cpp15
-rw-r--r--icespider/unittests/testRoutes.json7
-rw-r--r--icespider/unittests/xslt/transform.xslt14
-rw-r--r--icespider/xslt/Jamfile.jam22
-rw-r--r--icespider/xslt/exslt.cpp17
-rw-r--r--icespider/xslt/xsltStreamSerializer.cpp70
-rw-r--r--icespider/xslt/xsltStreamSerializer.h34
15 files changed, 278 insertions, 8 deletions
diff --git a/icespider/common/routes.ice b/icespider/common/routes.ice
index 65e7d67..bc2406a 100644
--- a/icespider/common/routes.ice
+++ b/icespider/common/routes.ice
@@ -20,12 +20,21 @@ module IceSpider {
sequence<Parameter> Parameters;
+ class OutputSerializer {
+ string contentType;
+ string serializer;
+ StringSeq params;
+ };
+
+ sequence<OutputSerializer> OutputSerializers;
+
class Route {
string name;
string path;
HttpMethod method = GET;
string operation;
Parameters params;
+ OutputSerializers outputSerializers;
};
sequence<Route> Routes;
diff --git a/icespider/compile/routeCompiler.cpp b/icespider/compile/routeCompiler.cpp
index 65b3141..8c274d8 100644
--- a/icespider/compile/routeCompiler.cpp
+++ b/icespider/compile/routeCompiler.cpp
@@ -193,6 +193,34 @@ namespace IceSpider {
}
void
+ RouteCompiler::registerOutputSerializers(FILE * output, RoutePtr r) const
+ {
+ for (const auto & os : r->outputSerializers) {
+ auto bs = boost::algorithm::replace_all_copy(os->serializer, ".", "::");
+ auto slash = os->contentType.find('/');
+ fprintbf(4, output, "addRouteSerializer({ \"%s\", \"%s\" }, new %s::IceSpiderFactory(",
+ os->contentType.substr(0, slash), os->contentType.substr(slash + 1), bs);
+ for (auto p = os->params.begin(); p != os->params.end(); ++p) {
+ if (p != os->params.begin()) {
+ fprintf(output, ", ");
+ }
+ fputs(p->c_str(), output);
+ }
+ fprintf(output, "));\n");
+ }
+ }
+
+ void
+ RouteCompiler::releaseOutputSerializers(FILE * output, RoutePtr r) const
+ {
+ for (const auto & os : r->outputSerializers) {
+ auto slash = os->contentType.find('/');
+ fprintbf(4, output, "removeRouteSerializer({ \"%s\", \"%s\" });\n",
+ os->contentType.substr(0, slash), os->contentType.substr(slash + 1));
+ }
+ }
+
+ void
RouteCompiler::processConfiguration(FILE * output, RouteConfigurationPtr c, const Units & units) const
{
fprintf(output, "// This source files was generated by IceSpider.\n");
@@ -200,6 +228,7 @@ namespace IceSpider {
fprintf(output, "// Standard headers.\n");
fprintf(output, "#include <irouteHandler.h>\n");
+ fprintf(output, "#include <slicer/serializer.h>\n");
fprintf(output, "\n// Interface headers.\n");
for (const auto & s : c->slices) {
@@ -257,6 +286,11 @@ namespace IceSpider {
}
fprintf(output, "\n");
fprintbf(3, output, "{\n");
+ registerOutputSerializers(output, r);
+ fprintbf(3, output, "}\n\n");
+ fprintbf(3, output, "~%s()\n", r->name);
+ fprintbf(3, output, "{\n");
+ releaseOutputSerializers(output, r);
fprintbf(3, output, "}\n\n");
fprintbf(3, output, "void execute(IceSpider::IHttpRequest * request) const\n");
fprintbf(3, output, "{\n");
diff --git a/icespider/compile/routeCompiler.h b/icespider/compile/routeCompiler.h
index af5912e..93522fd 100644
--- a/icespider/compile/routeCompiler.h
+++ b/icespider/compile/routeCompiler.h
@@ -26,6 +26,8 @@ namespace IceSpider {
private:
void processConfiguration(FILE * output, RouteConfigurationPtr, const Units &) const;
+ void registerOutputSerializers(FILE * output, RoutePtr) const;
+ void releaseOutputSerializers(FILE * output, RoutePtr) const;
static Slice::OperationPtr findOperation(RoutePtr, const Units &);
static Slice::OperationPtr findOperation(RoutePtr, const Slice::ContainerPtr &, const Ice::StringSeq & = Ice::StringSeq());
};
diff --git a/icespider/core/core.cpp b/icespider/core/core.cpp
index 85a9bfa..b21a805 100644
--- a/icespider/core/core.cpp
+++ b/icespider/core/core.cpp
@@ -12,7 +12,6 @@ namespace IceSpider {
// Initialize routes
for (const auto & rp : AdHoc::PluginManager::getDefault()->getAll<IRouteHandler>()) {
auto r = rp->implementation();
- r->initialize();
auto & mroutes = routes[r->method];
if (mroutes.size() <= r->pathElementCount()) {
mroutes.resize(r->pathElementCount() + 1);
diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp
index dcbab7d..a0d8fcd 100644
--- a/icespider/core/irouteHandler.cpp
+++ b/icespider/core/irouteHandler.cpp
@@ -9,11 +9,6 @@ namespace IceSpider {
Path(p),
method(m)
{
- }
-
- void
- IRouteHandler::initialize()
- {
auto globalSerializers = AdHoc::PluginManager::getDefault()->getAll<Slicer::StreamSerializerFactory>();
for (const auto & gs : globalSerializers) {
auto slash = gs->name.find('/');
@@ -21,6 +16,10 @@ namespace IceSpider {
}
}
+ IRouteHandler::~IRouteHandler()
+ {
+ }
+
Ice::ObjectPrx
IRouteHandler::getProxy(IHttpRequest * request, const char * type) const
{
@@ -44,5 +43,21 @@ namespace IceSpider {
return Slicer::StreamSerializerFactory::createNew(
"application/json", strm);
}
+
+ void
+ IRouteHandler::addRouteSerializer(const ContentType & ct, StreamSerializerFactoryPtr ssfp)
+ {
+ routeSerializers.insert({ ct, ssfp });
+ }
+
+ void
+ IRouteHandler::removeRouteSerializer(const ContentType & ct)
+ {
+ auto i = routeSerializers.find(ct);
+ if (i != routeSerializers.end()) {
+ delete i->second;
+ routeSerializers.erase(i);
+ }
+ }
}
diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h
index 6ee8160..1cfa5ee 100644
--- a/icespider/core/irouteHandler.h
+++ b/icespider/core/irouteHandler.h
@@ -13,9 +13,9 @@ namespace IceSpider {
class DLL_PUBLIC IRouteHandler : public AdHoc::AbstractPluginImplementation, public Path {
public:
IRouteHandler(HttpMethod, const std::string & path);
+ virtual ~IRouteHandler();
virtual void execute(IHttpRequest * request) const = 0;
- virtual void initialize();
virtual Slicer::SerializerPtr getSerializer(const char *, const char *, std::ostream &) const;
virtual Slicer::SerializerPtr defaultSerializer(std::ostream &) const;
@@ -41,6 +41,9 @@ namespace IceSpider {
{
return Interface::ProxyType::uncheckedCast(getProxy(request, typeid(Interface).name()));
}
+
+ void addRouteSerializer(const ContentType &, StreamSerializerFactoryPtr);
+ void removeRouteSerializer(const ContentType &);
};
typedef AdHoc::PluginOf<IRouteHandler> RouteHandlers;
}
diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam
index 234e715..05fecb4 100644
--- a/icespider/unittests/Jamfile.jam
+++ b/icespider/unittests/Jamfile.jam
@@ -46,6 +46,8 @@ run
<library>test-api
<library>dl
<library>adhocutil
+ <library>slicer-xml
+ <library>../xslt//icespider-xslt
<library>../core//icespider-core
<library>../compile//icespider-compile
<implicit-dependency>../compile//icespider-compile
@@ -56,7 +58,9 @@ run
run
testApp.cpp
testRoutes.json
- : : :
+ : :
+ xslt/transform.xslt
+ :
<define>BOOST_TEST_DYN_LINK
<icespider>../compile//icespider
<library>testCommon
@@ -67,6 +71,7 @@ run
<library>slicer-xml
<library>../common//icespider-common
<library>../core//icespider-core
+ <library>../xslt//icespider-xslt
<implicit-dependency>../common//icespider-common
<implicit-dependency>test-api
<dependency>../compile
diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp
index bb7c3c6..fb79b0a 100644
--- a/icespider/unittests/testApp.cpp
+++ b/icespider/unittests/testApp.cpp
@@ -9,9 +9,17 @@
#include <Ice/ObjectAdapter.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <definedDirs.h>
using namespace IceSpider;
+static void forceEarlyChangeDir() __attribute__((constructor(101)));
+void forceEarlyChangeDir()
+{
+ boost::filesystem::current_path(XSTR(ROOT));
+}
+
BOOST_AUTO_TEST_CASE( testLoadConfiguration )
{
BOOST_REQUIRE_EQUAL(6, AdHoc::PluginManager::getDefault()->getAll<IRouteHandler>().size());
@@ -253,6 +261,22 @@ BOOST_AUTO_TEST_CASE( testCallIndexAcceptXml )
BOOST_REQUIRE_EQUAL(requestXml.output.str(), "Status: 200 OK\r\n\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<SomeModel><value>index</value></SomeModel>\n");
}
+BOOST_AUTO_TEST_CASE( testCallIndexAcceptTextHtml )
+{
+ TestRequest requestHtml(this, HttpMethod::GET, "/");
+ requestHtml.hdr["Accept"] = "text/html";
+ process(&requestHtml);
+ BOOST_REQUIRE_EQUAL(requestHtml.output.str(), "Status: 200 OK\r\n\r\n<html><head><title>Some Model</title></head><body><p><b>value</b>: index</p></body></html>\n");
+}
+
+BOOST_AUTO_TEST_CASE( testCallViewSomethingAcceptHtml )
+{
+ TestRequest requestHtml(this, HttpMethod::GET, "/view/something/1234");
+ requestHtml.hdr["Accept"] = "text/html";
+ process(&requestHtml);
+ BOOST_REQUIRE_EQUAL(requestHtml.output.str(), "Status: 406 Unacceptable\r\n\r\n");
+}
+
BOOST_AUTO_TEST_CASE( testCallIndexAcceptNotSupported )
{
TestRequest requestBadAccept(this, HttpMethod::GET, "/");
diff --git a/icespider/unittests/testCompile.cpp b/icespider/unittests/testCompile.cpp
index 411f703..46175ef 100644
--- a/icespider/unittests/testCompile.cpp
+++ b/icespider/unittests/testCompile.cpp
@@ -10,6 +10,12 @@
using namespace IceSpider;
+static void forceEarlyChangeDir() __attribute__((constructor(101)));
+void forceEarlyChangeDir()
+{
+ boost::filesystem::current_path(XSTR(ROOT));
+}
+
class CoreFixture {
protected:
CoreFixture() :
@@ -73,9 +79,18 @@ BOOST_AUTO_TEST_CASE( testCompile )
auto compileCommand = boost::algorithm::join<std::vector<std::string>>({
"gcc", "-c", "-std=c++1y", "-fPIC", "-fvisibility=hidden", "-O3",
"-I", "/usr/include/adhocutil",
+ "-I", "/usr/include/glib-2.0",
+ "-I", "/usr/include/glibmm-2.4",
+ "-I", "/usr/include/libxml2",
+ "-I", "/usr/include/libxml++-2.6",
+ "-I", "/usr/include/libxslt",
"-I", "/usr/include/slicer",
+ "-I", "/usr/lib/glib-2.0/include",
+ "-I", "/usr/lib/glibmm-2.4/include",
+ "-I", "/usr/lib/libxml++-2.6/include",
"-I", (rootDir.parent_path() / "core").string(),
"-I", (rootDir.parent_path() / "common").string(),
+ "-I", (rootDir.parent_path() / "xslt").string(),
"-I", (rootDir.parent_path() / "common" / "bin" / modeDir / "allow-ice-yes").string(),
"-I", libGenDir.string(),
"-o", outputo.string(),
diff --git a/icespider/unittests/testRoutes.json b/icespider/unittests/testRoutes.json
index cf5d289..921d983 100644
--- a/icespider/unittests/testRoutes.json
+++ b/icespider/unittests/testRoutes.json
@@ -6,6 +6,13 @@
"name": "index",
"path": "/",
"method": "GET",
+ "outputSerializers": [
+ {
+ "contentType": "text/html",
+ "serializer": "IceSpider.XsltStreamSerializer",
+ "params": [ "\"xslt/transform.xslt\"" ]
+ }
+ ],
"operation": "TestIceSpider.TestApi.index"
},
{
diff --git a/icespider/unittests/xslt/transform.xslt b/icespider/unittests/xslt/transform.xslt
new file mode 100644
index 0000000..b70c90c
--- /dev/null
+++ b/icespider/unittests/xslt/transform.xslt
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:output encoding="utf-8" method="html" media-type="text/html" indent="yes"/>
+ <xsl:template match="/SomeModel">
+ <html>
+ <head>
+ <title>Some Model</title>
+ </head>
+ <body>
+ <p><b>value</b>: <xsl:value-of select="value"/></p>
+ </body>
+ </html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/icespider/xslt/Jamfile.jam b/icespider/xslt/Jamfile.jam
new file mode 100644
index 0000000..1bf62a8
--- /dev/null
+++ b/icespider/xslt/Jamfile.jam
@@ -0,0 +1,22 @@
+lib adhocutil : : : : <include>/usr/include/adhocutil ;
+lib slicer-xml : : : : <include>/usr/include/slicer ;
+lib boost_system ;
+lib IceUtil ;
+lib xslt ;
+lib exslt ;
+
+lib icespider-xslt :
+ [ glob-tree *.cpp : bin ]
+ :
+ <library>adhocutil
+ <library>slicer-xml
+ <library>boost_system
+ <library>IceUtil
+ <library>xslt
+ <library>exslt
+ <library>../..//libxmlpp
+ : :
+ <include>.
+ <library>../..//libxmlpp
+ ;
+
diff --git a/icespider/xslt/exslt.cpp b/icespider/xslt/exslt.cpp
new file mode 100644
index 0000000..63bd0d1
--- /dev/null
+++ b/icespider/xslt/exslt.cpp
@@ -0,0 +1,17 @@
+#include <libexslt/exslt.h>
+#include <libxslt/transform.h>
+
+static void initLibXml() __attribute__((constructor(102)));
+void initLibXml()
+{
+ xmlInitParser();
+ exsltRegisterAll();
+}
+
+static void cleanupLibXml() __attribute__((destructor(102)));
+void cleanupLibXml()
+{
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+}
+
diff --git a/icespider/xslt/xsltStreamSerializer.cpp b/icespider/xslt/xsltStreamSerializer.cpp
new file mode 100644
index 0000000..bbe3943
--- /dev/null
+++ b/icespider/xslt/xsltStreamSerializer.cpp
@@ -0,0 +1,70 @@
+#include "xsltStreamSerializer.h"
+#include <libxslt/xsltInternals.h>
+#include <libxml/HTMLtree.h>
+#include <factory.impl.h>
+
+namespace IceSpider {
+ static int xmlstrmclosecallback(void * context)
+ {
+ ((std::ostream*)context)->flush();
+ return 0;
+ }
+
+ static int xmlstrmwritecallback(void * context, const char * buffer, int len)
+ {
+ ((std::ostream*)context)->write(buffer, len);
+ return len;
+ }
+
+ XsltStreamSerializer::IceSpiderFactory::IceSpiderFactory(const char * path) :
+ stylesheet(xsltParseStylesheetFile(BAD_CAST path))
+ {
+ if (!stylesheet) {
+ throw xmlpp::exception("Failed to load stylesheet");
+ }
+ }
+
+ XsltStreamSerializer::IceSpiderFactory::~IceSpiderFactory()
+ {
+ xsltFreeStylesheet(stylesheet);
+ }
+
+ XsltStreamSerializer *
+ XsltStreamSerializer::IceSpiderFactory::create(std::ostream & strm) const
+ {
+ return new XsltStreamSerializer(strm, stylesheet);
+ }
+
+ XsltStreamSerializer::XsltStreamSerializer(std::ostream & os, xsltStylesheet * ss) :
+ Slicer::XmlDocumentSerializer(doc),
+ strm(os),
+ doc(nullptr),
+ stylesheet(ss)
+ {
+ }
+
+ XsltStreamSerializer::~XsltStreamSerializer()
+ {
+ delete doc;
+ }
+
+ void
+ XsltStreamSerializer::Serialize(Slicer::ModelPartPtr mp)
+ {
+ Slicer::XmlDocumentSerializer::Serialize(mp);
+ auto result = xsltApplyStylesheet(stylesheet, doc->cobj(), nullptr);
+ if (!result) {
+ throw xmlpp::exception("Failed to apply XSL transform");
+ }
+ xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &strm, NULL);
+ if (xmlStrcmp(stylesheet->method, BAD_CAST "html") == 0) {
+ htmlDocContentDumpFormatOutput(buf, result, (const char *) stylesheet->encoding, 0);
+ }
+ else {
+ xmlNodeDumpOutput(buf, result, result->children, 0, 0, (const char *) stylesheet->encoding);
+ }
+ xmlOutputBufferClose(buf);
+ xmlFreeDoc(result);
+ }
+}
+
diff --git a/icespider/xslt/xsltStreamSerializer.h b/icespider/xslt/xsltStreamSerializer.h
new file mode 100644
index 0000000..d8000d1
--- /dev/null
+++ b/icespider/xslt/xsltStreamSerializer.h
@@ -0,0 +1,34 @@
+#ifndef ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H
+#define ICESPIDER_CORE_XSLTSTREAMSERIALIZER_H
+
+#include <slicer/xml/serializer.h>
+#include <visibility.h>
+#include <libxslt/transform.h>
+
+namespace IceSpider {
+ class DLL_PUBLIC XsltStreamSerializer : public Slicer::XmlDocumentSerializer {
+ public:
+ class IceSpiderFactory : public Slicer::StreamSerializerFactory {
+ public:
+ IceSpiderFactory(const char *);
+ ~IceSpiderFactory();
+
+ XsltStreamSerializer * create(std::ostream &) const override;
+
+ xsltStylesheet * stylesheet;
+ };
+
+ XsltStreamSerializer(std::ostream &, xsltStylesheet *);
+ ~XsltStreamSerializer();
+
+ void Serialize(Slicer::ModelPartPtr mp) override;
+
+ protected:
+ std::ostream & strm;
+ xmlpp::Document * doc;
+ xsltStylesheet * stylesheet;
+ };
+}
+
+#endif
+