nitrestful: generate code for async restful methods
authorAlexis Laferrière <alexis.laf@xymus.net>
Fri, 14 Oct 2016 18:41:46 +0000 (14:41 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Sat, 4 Feb 2017 21:08:38 +0000 (16:08 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

lib/nitcorn/restful.nit
src/nitrestful.nit
tests/sav/nitrestful_args1.res

index e46bf13..abaaf0d 100644 (file)
@@ -53,8 +53,8 @@
 module restful is new_annotation(restful)
 
 import nitcorn
-import json
-import pthreads
+private import json
+import pthreads::threadpool
 
 # Action with `restful` methods
 class RestfulAction
@@ -82,6 +82,9 @@ class RestfulAction
 
                return obj
        end
+
+       # Thread pool used by methods annotated with `restful(async)`
+       var thread_pool = new ThreadPool is writable
 end
 
 # Thread dedicated to a single `request`
index 68e8258..8f0c3ae 100644 (file)
@@ -212,6 +212,9 @@ for mclass in phase.restful_classes do
        var t = new Template
        nit_module.content.add t
 
+       var classes = new Template
+       nit_module.content.add classes
+
        t.add """
 redef class {{{mclass}}}
        redef fun prepare_respond_and_close(request, truncated_uri, http_server)
@@ -279,12 +282,50 @@ redef class {{{mclass}}}
                var sig = ""
                if args.not_empty then sig = "({args.join(", ")})"
 
-               t.add """
+               if not method.restful_async then
+                       # Synchronous method
+                       t.add """
                                var response = {{{method.name}}}{{{sig}}}
                                http_server.respond response
                                http_server.close
                                return
 """
+               else
+                       # Asynchronous method
+                       var task_name = "Task_{mclass}_{method.name}"
+                       args.unshift "http_server"
+                       args.unshift "request"
+                       args.unshift "self"
+
+                       t.add """
+                               var task = new {{{task_name}}}({{{args.join(", ")}}})
+                               self.thread_pool.execute task
+                               return
+"""
+
+                       var thread_attribs = new Array[String]
+                       for param in msig.mparameters do
+                               thread_attribs.add """
+       private var out_{{{param.name}}}: {{{param.mtype}}}"""
+                       end
+
+                       classes.add """
+
+# Generated task to execute {{{mclass}}}::{{{method.name}}}
+class {{{task_name}}}
+       super RestfulTask
+
+       redef type A: {{{mclass}}}
+
+{{{thread_attribs.join("\n")}}}
+
+       redef fun indirect_restful_method
+       do
+               return action.{{{method.name}}}{{{sig}}}
+       end
+end
+"""
+               end
 
                if isas.not_empty then t.add """
                        end
index c85bd0f..ea30917 100644 (file)
@@ -7,12 +7,15 @@ end
 import restful_annot
 
 redef class MyAction
-       redef fun answer(request, truncated_uri)
+       redef fun prepare_respond_and_close(request, truncated_uri, http_server)
        do
                var resources = truncated_uri.split("/")
                if resources.not_empty and resources.first.is_empty then resources.shift
 
-               if resources.length != 1 then return super
+               if resources.length != 1 then
+                       super
+                       return
+               end
                var resource = resources.first
 
                if (resource == "foo") then
@@ -20,30 +23,74 @@ redef class MyAction
                        var out_s = in_s
 
                        var in_i = request.string_arg("i")
-                       var out_i = deserialize_arg(in_i)
+                       var out_i = deserialize_arg(in_i, "Int")
 
                        var in_b = request.string_arg("b")
-                       var out_b = deserialize_arg(in_b)
+                       var out_b = deserialize_arg(in_b, "Bool")
 
-                       if not out_s isa String or not out_i isa Int or not out_b isa Bool then
-                               return super
+                       if out_s isa String and out_i isa Int and out_b isa Bool then
+                               var response = foo(out_s, out_i, out_b)
+                               http_server.respond response
+                               http_server.close
+                               return
                        end
-                       return foo(out_s, out_i, out_b)
-               else if (resource == "api_name" or resource == "alt_name") and (request.method == "GET" or request.method == "PUT") then
+               end
+               if (resource == "api_name" or resource == "alt_name") and (request.method == "GET" or request.method == "PUT") then
                        var in_s = request.string_arg("s")
                        var out_s = in_s
 
                        var in_i = request.string_arg("i")
-                       var out_i = deserialize_arg(in_i)
+                       var out_i = deserialize_arg(in_i, "nullable Int")
 
                        var in_b = request.string_arg("b")
-                       var out_b = deserialize_arg(in_b)
+                       var out_b = deserialize_arg(in_b, "nullable Bool")
+
+                       if out_i isa nullable Int and out_b isa nullable Bool then
+                               var response = bar(out_s, out_i, out_b)
+                               http_server.respond response
+                               http_server.close
+                               return
+                       end
+               end
+               if (resource == "async_service") then
+                       var in_str = request.string_arg("str")
+                       var out_str = in_str
+
+                       if out_str isa String then
+                               var task = new Task_MyAction_async_service(self, request, http_server, out_str)
+                               self.thread_pool.execute task
+                               return
+                       end
+               end
+               if (resource == "complex_args") then
+                       var in_array = request.string_arg("array")
+                       var out_array = deserialize_arg(in_array, "Array[String]")
+
+                       var in_data = request.string_arg("data")
+                       var out_data = deserialize_arg(in_data, "MyData")
 
-                       if not out_i isa nullable Int or not out_b isa nullable Bool then
-                               return super
+                       if out_array isa Array[String] and out_data isa MyData then
+                               var response = complex_args(out_array, out_data)
+                               http_server.respond response
+                               http_server.close
+                               return
                        end
-                       return bar(out_s, out_i, out_b)
                end
-               return super
+               super
        end
 end
+
+# Generated task to execute MyAction::async_service
+class Task_MyAction_async_service
+       super RestfulTask
+
+       redef type A: MyAction
+
+       private var out_str: String
+
+       redef fun indirect_restful_method
+       do
+               return action.async_service(out_str)
+       end
+end
+