diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-08-21 13:06:27 +0100 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-08-21 13:06:27 +0100 | 
| commit | b388a5537f2e3b459021118fcda18e2a58da3492 (patch) | |
| tree | 52f8beda2b103d5411b63cef900060a7433708ae | |
| parent | Allow inclusion of user headers (diff) | |
| download | icespider-b388a5537f2e3b459021118fcda18e2a58da3492.tar.bz2 icespider-b388a5537f2e3b459021118fcda18e2a58da3492.tar.xz icespider-b388a5537f2e3b459021118fcda18e2a58da3492.zip | |
Add support for custom output serializers
| -rw-r--r-- | icespider/common/routes.ice | 9 | ||||
| -rw-r--r-- | icespider/compile/routeCompiler.cpp | 34 | ||||
| -rw-r--r-- | icespider/compile/routeCompiler.h | 2 | ||||
| -rw-r--r-- | icespider/core/core.cpp | 1 | ||||
| -rw-r--r-- | icespider/core/irouteHandler.cpp | 25 | ||||
| -rw-r--r-- | icespider/core/irouteHandler.h | 5 | ||||
| -rw-r--r-- | icespider/unittests/Jamfile.jam | 7 | ||||
| -rw-r--r-- | icespider/unittests/testApp.cpp | 24 | ||||
| -rw-r--r-- | icespider/unittests/testCompile.cpp | 15 | ||||
| -rw-r--r-- | icespider/unittests/testRoutes.json | 7 | ||||
| -rw-r--r-- | icespider/unittests/xslt/transform.xslt | 14 | ||||
| -rw-r--r-- | icespider/xslt/Jamfile.jam | 22 | ||||
| -rw-r--r-- | icespider/xslt/exslt.cpp | 17 | ||||
| -rw-r--r-- | icespider/xslt/xsltStreamSerializer.cpp | 70 | ||||
| -rw-r--r-- | icespider/xslt/xsltStreamSerializer.h | 34 | 
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 + | 
