Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 194ecb1deff999a73123c75d3607657c99cbcf36 @nathell committed Feb 26, 2012
Showing with 111 additions and 0 deletions.
  1. +19 −0 LICENSE
  2. +58 −0 README.md
  3. +4 −0 project.clj
  4. +30 −0 src/pl/danieljanus/jsonrpc.clj
19 LICENSE
@@ -0,0 +1,19 @@
+clj-json-rpc -- Copyright (C) 2010, 2011, 2012 by Daniel Janus
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
@@ -0,0 +1,58 @@
+# clj-json-rpc
+
+clj-json-rpc is a Clojure library that makes it easy to create web services
+using the [JSON-RPC][1] protocol and [Ring][2].
+
+The current version is 0.1. Just add this to your `project.clj`:
+
+ [clj-json-rpc "0.1"]
+
+With clj-json-rpc, you just define your JSON-RPC methods as normal Clojure
+functions, just substituting `defn-json-rpc` macro for `defn`. These methods
+are in fact normal Clojure functions: for instance, you can call them at the
+REPL or from other functions. The only way they're special is that they have
+a `:json-rpc` key set to `true` in their metadata.
+
+clj-json-rpc includes a Ring handler that you can use in your server, called
+`process-json-rpc`. Typically you won't install it directly but instead invoke
+it from your toplevel handler when some condition is satisfied, like this:
+
+ (defn toplevel-handler [req]
+ (if (= (:uri req) "/json-rpc")
+ (process-json-rpc req)
+ (do-something-else req)))
+
+The handler parses the request's body as JSON, extracts the necessary
+fields as mandated by the JSON-RPC spec, calls the desired function
+(as long as it has been defined using `defn-json-rpc`) and serializes
+the output back to JSON. If the function throws an exception, it will
+be reported as a JSON-RPC error.
+
+Caveat: clj-json-rpc is currently very basic and limited in several ways.
+One limitation is that you must define all your methods in the same namespace,
+or else the handler won't be able to locate them.
+
+clj-json-rpc was originally part of the source of [Smyrna][3], a simple
+concordancer for Polish, but was factored out since it is going to be used
+in other projects as well. See that project for a usage example as well as
+a sample of client code (in CoffeeScript).
+
+## Example
+
+ (use 'pl.danieljanus.jsonrpc)
+
+ (defn-json-rpc example [x]
+ (+ x 42)
+ ;=> #'user/example
+
+ ; simulate a request by directly calling the handler
+ (process-json-rpc
+ {:body (java.io.ByteArrayInputStream.
+ (.getBytes "{\"method\":\"example\",\"params\":[2]}"))})
+ ;=> {:status 200,
+ :headers {"Content-Type" "application/json; charset=utf-8"},
+ :body "{\"result\":44}"}
+
+ [1]: http://json-rpc.org/
+ [2]: http://github.com/mmcgrana/ring
+ [3]: http://github.com/nathell/smyrna
@@ -0,0 +1,4 @@
+(defproject clj-json-rpc "0.1"
+ :description "A JSON-RPC server for Clojure, suitable for use with Ring."
+ :dependencies [[org.clojure/clojure "1.2.1"]
+ [org.clojure/data.json "0.1.2"]])
@@ -0,0 +1,30 @@
+(ns pl.danieljanus.jsonrpc
+ (:require [clojure.data.json :as json]))
+
+(defn answer-json [obj]
+ {:status 200
+ :headers {"Content-Type" "application/json; charset=utf-8"}
+ :body (json/json-str obj)})
+
+(defmacro assert-msg [x msg]
+ `(when-not ~x
+ (throw (Exception. ~msg))))
+
+(def *json-rpc-ns* nil)
+
+(defn process-json-rpc [req]
+ (let [{:keys [id method params]} (-> req :body slurp json/read-json)
+ idify (if id #(assoc % :id id) identity)]
+ (try
+ (let [method (symbol method)
+ f (ns-resolve *json-rpc-ns* method)
+ _ (assert-msg (:json-rpc (meta f)) "Method not available")]
+ (answer-json (idify {:result (apply f params)})))
+ (catch Exception e
+ (.printStackTrace e)
+ (answer-json (idify {:error (.getMessage e)}))))))
+
+(defmacro defn-json-rpc [name & rest]
+ `(do
+ (alter-var-root #'*json-rpc-ns* (constantly *ns*))
+ (defn ~name {:json-rpc true} ~@rest)))

0 comments on commit 194ecb1

Please sign in to comment.