How to start immutant message queue? - clojure

From the immutant documentation at http://immutant.org/documentation/current/apidoc/guide-installation.html:
With the dependencies in place, you simply invoke the Immutant
services from your app’s main entry point, identified by the :main
key in your project.clj.
Immutant's web service can be invoked like:
(ns my-app.handler
...
(:require [immutant.web :as web])
... )
(def app ... )
(defn -main [& args]
(web/run app))
What's the equivalent of (web/run app) for immutant.messaging?

here your answer
to start queue
(ns my-project.name
(:require [immutant.messaging :as msg]))
(defn call-fn
[args]
(let [fun (ns-resolve "namespace where fun defined" (symbol (first args)))
params (rest args)]
(if (seq params)
(apply fun )
(fun))))
(msg/start "queue")
(msg/respond "queue" (fn [args] (call-fn args)))
to call the queue just say
#(msg/request "queue" ["fun-name" params])

Related

Parse Clojure Command Line Args

I am trying to separate my cli options into a stand-alone namespace for starting up an HTTP server, and I am getting this error-
clojure.lang.ArraySeq cannot be cast to java.lang.CharSequence
In main.clj, this code works fine-
(ns served.main
(:require [org.httpkit.server :refer [run-server]]
[served.app.core :refer [handler]]
[served.server.cli-options :refer [set-options]]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn -main [& args]
(println "Server starting")
(let [options (get (parse-opts args cli-options) :options)]
;;(let [options (set-options args)]
(println (str options))
(run-server handler options)))
It will work with the default options in (def cli-options) and it compiles correctly if I pass in arguments, such as -p 7000.
When I call the main function with the external namespace served.server.cli-options instead of clojure.tools.cli directly (i.e. switch the comment in main), I get the error only when passing in args.
That is, starting the server without arguments, e.g. lein run compiles fine and will print out the defaults. The error comes with lein run -p 7000.
After deleting (def cli-options) in main to avoid any global conflict, here is served.server.cli-options
(ns served.server.cli-options
(:require [clojure.tools.cli :refer [parse-opts]]))
(def cli-options
[
["-p" "--port PORT" "Port number"
:default 5000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
])
(defn set-options [& args]
(let [options (get (parse-opts args cli-options) :options)]
(println (str options))
options))
So far as I can tell, I copied the contents to the new namespace correctly. Here are the docs for parse-opts, here is the example that I am drawing from, and a similar but different SO issue here.
My question - how are the CLI args being transformed to throw casting error, and how do I fix it?
Any help would be greatly appreciated.
Delete the & in:
(defn set-options [& args]
& wraps up any additional arguments in a seq. Since you’ve already wrapped the program arguments once in main, you mustn’t do it again in the call to set-options.

RabbitMQ Clojure Langohr doesn't throw error when rabbitmq server restarted

I have the following code to monitoring database table to create MQ messsage every several seconds. And I found that when I restarted the RabbitMQ server the app still runs without throwing an exception, and it still print message was created. So why it won't throw an exception? And another question is how to close the connect when I kill the app? Since it's a service, I have no place to write the code to close the RabbitMQ connection.
(require '[clojure.java.jdbc :as j])
(require '[langohr.core :as rmq])
(require '[langohr.channel :as lch])
(require '[langohr.queue :as lq])
(require '[langohr.exchange :as le])
(require '[langohr.consumers :as lc])
(require '[langohr.basic :as lb])
(require '[clojure.data.json :as json])
(require '[clojure.java.io :as io])
(defn load-props
[file-name]
(with-open [^java.io.Reader reader (io/reader file-name)]
(let [props (java.util.Properties.)]
(.load props reader)
(into {} (for [[k v] props] [(keyword k) (read-string v)])))))
(def ^{:const true}
default-exchange-name "")
(defn create-message-from-database
([channel qname db-spec]
(let [select-sql "select a.file_id from file_store_metadata
where processed=false "
results (j/query db-spec select-sql)
]
(doseq [row results]
(let [file-id (:file_id row)]
(lb/publish channel default-exchange-name qname (json/write-str row) {:content-type "text/plain" :type "greetings.hi" :persistent true})
(j/update! db-spec :file_store_metadata {:processed true} ["file_id = ?" (:file_id row)])
(println "message created for a new file id" (:file_id row))))
))
)
(defn create-message-from-database-loop
([x channel qname db] (
while true
(Thread/sleep (* x 1000))
(create-message-from-database channel qname db)
))
)
(defn -main
[& args]
(let [
properties (load-props "resource.properties")
postgres-db (:database properties)
rabbitmq-url (:rabbitmq properties)
wake-up-interval (:interval properties)
rmq-conn (rmq/connect {:uri rabbitmq-url})
ch (lch/open rmq-conn)
qname "etl scheduler"]
(println "monitoring file store meta data on " postgres-db " every " wake-up-interval " seconds")
(println "message will be created on rabbitmq server" rabbitmq-url)
(lq/declare ch qname {:exclusive false :auto-delete false :persistent true})
(create-message-from-database-loop wake-up-interval ch qname postgres-db)
)
)

Hiccup template function

I'm trying to add the following Hiccup template function to my file
(defn d3-page [title js body & {:keys [extra-js] :or {extra-js []}}]
(html5
[:head
[:title title]
(include-css "/css/nv.d3.css"))
(include-css "/css/style.css")]
[:body
(concat
[body]
[(include-js "http://d3js.org/d3.v3.min.js")
(include-js (str "https://raw.github.com"
"/novus/nvd3"
"/master/nv.d3.min.js")]
(map include-js extra-js)
[(include-js "/js/script.js")
(javascript-tag js)])]))
but keep getting an unmatched delimiter when I run lein ring server. This comes from Clojure Data Cookbook, so I am surprised to find an error and suspect the error is just on my end. Below is the rest of the code in the file:
(ns web-viz.web
(:require [compojure.route :as route]
[compojure.handler :as handler]
[clojure.string :as str])
(:use compojure.core
ring.adapter.jetty
[ring.middleware.content-type :only
(wrap-content-type)]
[ring.middleware.file :only (wrap-file)]
[ring.middleware.file-info :only
(wrap-file-info)]
[ring.middleware.stacktrace :only
(wrap-stacktrace)]
[ring.util.response :only (redirect)]
[hiccup core element page]
[hiccup.middleware :only (wrap-base-url)]))
(defn d3-page...as above
...)
(deftype Group [key values])
(deftype Point [x y size])
(defn add-label [chart axis label]
(if-not (nil? label)
(.axisLabel (aget chart axis) label)))
(defn add-axes-labels [chart x-label y-label]
(doto chart (add-label "xAxis" x-label)
(add-label "yAxis" y-label)))
(defn populate-node [selector chart groups transition continuation]
(-> (.select js/d3 selector)
(.datum groups)
(.transition)
(.duration (if transition 500 0))
(.call chart)
(.call continuation)))
(defn force-layout-plot []
(d3-page "Force-Directed Layout"
"webviz.force.force_layout();"
[:div#force.chart [:svg]]))
(defroutes site-routes
(GET "/force" [] (force-layout-plot))
(GET "/force/data.json" []
(redirect "/data/census-race.json"))
(route/resources "/")
(route/not-found "Page not found"))
(def app (-> (handler/site site-routes)))
In line 5
(include-css "/css/nv.d3.css"))
There's an extra ) there.
And in line 13,
"/master/nv.d3.min.js")]
There's one ) missing. Should be
"/master/nv.d3.min.js"))]
You should use an editor which can do the matching of braces, parentheses, and brackets, etc. automatically.

My logging (with robert-hooke) does not work properly with tools.namespace/refresh, why?

EDIT: Turned out I was using require instead of :require in the namespace declaration. With :require, tools.namespace refreshes the logging namespace, and the problem goes away. I still find it curious, however, that the expression (eval `(var ~(symbol "A/func"))) does not work in the situation described below (that is, if B below is not refreshed).
Summary: I'm using tools.namespace. If I have namespaces A and B, and in B do (eval `(var ~(symbol "A/func"))), and (tools.namespace/refresh) and run the code, that works. But if I make a change to A, do (tools.namespace/refresh), so that only A refreshes, then running that expression gives the error: Cannot resolve var: A/func in this context, even though A/func exists. Why?
Longer version:
In my project, I have a logging module/namespace that uses robert-hooke (see below). I'm using tools.namespace to reload my code when I make changes.
The problem is the following: When I want to log (my logging currently just prints) something, I list the functions that I want to log in my logging namespace and do (t.n/refresh). That works. But if I make changes to the the namespaces that contain the functions that I want to log, and do (t.n/refresh) without making changes to the logging namespace, the logging no longer works (for the functions that have been refreshed). As soon as I make a change to logging, so that it too is refreshed by tools.namespace, it starts working again.
So, it's like the vars in namespaces that have been refreshed don't properly get their logging hooks. But I don't understand why.
Below is my logging namespace. I call add-logging-wrappers each time I run my program.
If I add (eval `(var ~(symbol "sv/register-damage"))) inside add-logging-wrappers, that's fine when logging has just been refreshed and the logging works. But those times the logging does not work, that expression causes the error Cannot resolve var: sv/register-damage in this context.
(ns game.logging
(require [robert.hooke :as rh]
[clojure.pprint :as pp]
[game.server.core :as sv]
[game.client.core :as cl]
[game.math :as math]
(game.common [core-functions :as ccfns]
[graphics :as gfx])
(game.server [pathfinding :as pf]))
(:use [game.utils]))
(defn log-println [name type object]
(println (str (current-thread-name) " // " name " " type ":\n"
(with-out-str
(pp/pprint object)))))
(defn print-output [name f & args]
(let [result (apply f args)]
(log-println name "output" result)
result))
(defn print-input [name f & args]
(log-println name "input" args)
(apply f args))
(defn print-call [name f & args]
(println (str (current-thread-name) "//" name))
(apply f args))
(defmacro make-name-var-list [fn-list]
`[~#(for [fn fn-list]
[(str fn) `(var ~fn)])])
(defmacro defloglist [name & fns]
`(def ~name (make-name-var-list [~#fns])))
(defn add-hooks [name-vars & wrappers]
(when (seq wrappers)
(doseq [[name var] name-vars]
(rh/add-hook var (partial (first wrappers) name)))
(recur name-vars (next wrappers))))
(defn get-ns-name-vars [ns-sym]
(-> (the-ns ns-sym) (#(.name %)) ns-interns))
(defn add-hooks-to-ns [ns-sym & wrappers]
(apply add-hooks (get-ns-name-vars ns-sym) wrappers))
(defloglist log-both
sv/distribute-exp ;; <--- things to log
sv/register-damage
sv/give-exp)
(defloglist log-input)
(defloglist log-output)
(defn add-logging-wrappers []
(dorun (->> (all-ns) (map #(.name %)) (mapcat ns-interns) (map second)
(map rh/clear-hooks)))
(add-hooks log-both print-output print-input)
(add-hooks log-input print-input)
(add-hooks log-output print-output))

Confused about resolving of classname

two files
types.clj:
(ns test.types)
(defrecord Price [date price])
(defrecord ProductPrice [name prices])
core.clj (It's OK)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
core.clj (java.lang.IllegalArgumentException: Unable to resolve classname: ProductPrice)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(ProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr))))))))
core.clj (It's OK)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defrecord tProductPrice [name prices])
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(tProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
core.clj (java.lang.IllegalStateException: ->ProductPrice already refers to: #'test.types/->ProductPrice in namespace: test.core)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defrecord ProductPrice [name prices])
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(ProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
I totally confused about these exceptions. And I can't find any more usage about 'record' except some simplest examples from clojure.org and books.
Any help, Thank you very much!
defrecord creates a java class in the package named after the current namespace. (ProductPrice. ...) is a call to the constructor of that type; this is java interop - not a plain function call.
You cannot refer to a class defined outside of java.lang or the current namespace unless you explicitly import it or specify the full package name. This includes calling its constructor.
So, to fix the problem you need to import Price and ProductPrice.
(ns test.core (:import [test.types Price]))
(Price. ...)
or call the full class+package name:
(test.types.Price. ...)