Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starting points to morph regular Servlets coding to my DSL

Clojure offers a good Java interop. However, I really want to have this:

(servlet IndexServlet
  (service[parmas] ....)
  (do-post[params] ....)
  (do-get [params] ....))

(servlet-filter SecurityFilter
  (do-filter [params] ....))

I guess that is what called a DSL and in Lisp world it is done via Macros.

I'm not sure how/where to start. refiy and extends forms are definitely have important role here but I don't know how that would fit into Macros.

How start doing this DSL?
A snippet, tips and tricks are really appreciated.

like image 970
Chiron Avatar asked Oct 21 '22 08:10

Chiron


1 Answers

You may want to look into Ring's Jetty adapter for an example of a servlet implementation in Clojure. The source is available here (link to source for the 1.1 release). In particular, the first function defined in that namespace, proxy-handler returns a handler based on an abstract class provided by Jetty.

If you choose to implement a similar approach (basing your servlet on a Java class providing some ready-made method impls), you'll need to use proxy; if you only need to implement interfaces (with no subclassing), then you'll probably want reify instead. Whether or not macros will be useful depends on which parts of the implementation will turn out to be fixed; Ring's Jetty adapter wouldn't benefit from the use of macros, but you may (for example if you wish to make the class to be extended / interface to be implemented into an argument, as the question seems to indicate).

In any case, whichever functionality you choose to implement will need to be part of an interface or protocol. So, implementing javax.servlet.Servlet plus an additional operation foo might look like this:

(import (javax.servlet Servlet ServletRequest ServletResponse))

(defprotocol PFoo
  (foo [this x y z]))

(reify
  Servlet
  (service [this ^ServletRequest req ^ServletResponse res]
    ...)
  ;; other Servlet methods here...
  PFoo
  (foo [this x y z]
    ...))

You could then wrap this in a macro to provide any desired syntactic sugar. Note that reify does not actually care about the way in which you interleave interface / protocol names and method definitions inside its body, so you could have your macro emit

(reify
  Servlet PFoo ... ; other interfaces & protocols
  (service [...] ...)
  (foo [...] ...)
  ;; other methods
  )

if that's more convenient.

A sketch of a macro taking a name of a servlet interface to implement (presumably extending javax.servlet.Servlet) and injecting a protocol with some extra methods:

(defprotocol PFancyServlet
  (do-get [this ...])
  (do-post [this ...]))

(defmacro servlet [servlet-iface & meths]
   `(reify ~servlet-iface PFancyServlet
      ~@meths))

meths will need to include do-get and do-post as well as servlet-iface methods; you could add some argument validation to make sure this is the case. An example call:

(servlet SomeServletInterface
  (service [this ...] ...)
  ;; ...
  (do-get [this ...] ...)
  (do-post [this ...] ...))
like image 70
Michał Marczyk Avatar answered Nov 03 '22 23:11

Michał Marczyk