next.jdbc - unable to find valid certification path to requested target - clojure

The error
; Execution error (SunCertPathBuilderException) at
sun.security.provider.certpath.SunCertPathBuilder/build
(SunCertPathBuilder.java:141). ; unable to find valid certification
path to requested target
The code (Clojure)
(ns backend.core
(:require [next.jdbc :as jdbc]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
(def db {:dbtype "sqlserver" :dbname "dbname" :user "MY_USER" :password "MY_PASSWORD" :host "MY_HOST" :port 1234})
(def datasource (jdbc/get-datasource db))
(defn create-product
"Create a product."
[name ds]
(jdbc/execute! ds [(str "insert into dbo.product (name) value('" name "')")]))
(comment
(create-product "my-product" datasource))
I'm playing around with clojure/sql server/next.jdbc and trying to work with my distant SQL Server 2017, but this error appear...
It look like I need some certificate. Is it the case? How can I generate it? How can I install it on my dev PC? Should it be install in a specific place?

I fixed my issue by changing the content of the (def db ...). Here is the updated code working.
(ns backend.core
(:require [next.jdbc :as jdbc]))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
(def db {:jdbcUrl "jdbc:sqlserver://MY_DISTANT_HOST:12345;databaseName=DB_NAME;user=USERNAME;password=MY_PASSWORD;encrypt=true;trustServerCertificate=true"})
(def datasource (jdbc/get-datasource db))
(defn create-product
"Create a product."
[name ds]
(with-open [connection (jdbc/get-connection ds)]
(jdbc/execute! connection ["insert into dbo.product (name) values (?)" name] {:return-keys true})))
(comment
(create-product "my-product" datasource))
You can find more information about jdbc connection string here: https://learn.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15
I too ran into a second issue after updating the data-source configuration. The error was:
no mssql-jdbc_auth-8.2.1.x64 in java.library.path
I only needed to download zip file here https://learn.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver15, unzip it and follow instructions inside install.txt and finally restart my IDE (VS Code).

Related

updated atom value not used in compojure route

I have a (routes (route/not-found)) definition with value derived from an (atom). Though I've updated the atom, the routing retains the initial value. This is similiar to Dynamic handler update in Clojure Ring/Compojure REPL but I'm having a hard time understanding what needs to be de/referenced where.
(ns mveroute
(:require
[org.httpkit.server :as srv]
[compojure.core :as cmpj]
[compojure.route :as route]
[clj-http.client :as client])
(:gen-class))
(def my-atom (atom "foobar"))
(def app
(cmpj/routes
(route/not-found {:status 400 :body #my-atom})))
(defn -main [& args]
(reset! my-atom "hello world")
(srv/run-server #'app {:port 8005})
;; "hello world" as expected
(println #my-atom)
;; still "foobar" but wanted "hello world"
(-> "http://localhost:8005"
(client/get {:throw-exceptions? false})
:body
println))
I thought warp-routes might have come to the rescue. But not how I've used it.
(defn atom-body [] {:status 200 :body #my-atom})
(cmpj/defroutes wrap-found
(route/not-found (atom-body)))
(def app
(cmpj/wrap-routes #'wrap-found {}))
The ultimate goal is a simple cli application that can set the resource/html root directory with command line arguments.
Try something like the following:
(defn not-found-fn
[req]
{:status 400 :body "not found again!"})
(def app
(cmpj/routes
(route/not-found not-found-fn)))
So inside of not-found-fn, you can construct the :body string any way you like. You could also have the string stored in an atom which is dereferenced by not-found-fn.
Side note:
Please see the following to clarify when you should use a Var object instead of just a symbol in your code:
When to use a Var instead of a function?

*ns* unexpectedly evaluate to 'user' namespace using 'lein run', but not when using 'lein repl'

I am trying to get the namespace associated with the code actually running.
I created a template project using 'lein new app test':
(ns test.core
(:gen-class))
(defn -main
[& args]
(println (ns-name *ns*)))
When using the repl, *ns* evaluates to test.core:
echo "(-main)" | lein repl
nREPL server started on port 37435 on host 127.0.0.1 - nrepl://127.0.0.1:37435
REPL-y 0.4.4, nREPL 0.7.0
Clojure 1.10.1
OpenJDK 64-Bit Server VM 1.8.0_265-b01
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
test.core=> (-main)
test.core
nil
test.core=> Bye for now!
But when using lein run, I get user namespace:
lein run
user
Why such a behavior ?
How can I get the namespace of a piece of code like in the repl ?
A few things to note:
*ns* is bound to the namespace wherein the expression is evaluated
user is the default namespace
when you call (test.core/-main) from the user namespace, *ns* is bound to user, not test.core.
If you would like to print the namespace in which -main was defined, you can use a top-level expression:
(ns test.core)
(def current-ns *ns*)
(defn -main [& args]
(println (ns-name current-ns)))
or use var metadata:
(ns test.core)
(defn -main [& args]
(println (ns-name (:ns (meta #'-main)))))

Clojure throws an exception when I don't expect it to

I have this code to get data from sumo logic and other services.
core.clj has this, which parses the arguments and routes it to the right function in route.clj
(def cli-options
[
["-a" "--app APPNAME" "set app. app can be:
sumologic or jira"]
["-?" "--help"]
])
(defn -main
[& args]
(let [{:keys [options summary errors arguments]} (parse-opts args cli-options)]
(cond
(:app options) (route/to (:app options) options arguments)
:else (print_usage summary))))
route.clj has this:
(defn to
[app options arguments]
(case app
"jira" (jira/respond options arguments)
"sumologic" (sumo/respond)))
And then sumo.clj has this. there are other functions, of course, but showing just the relevant parts.
(defn get-env-var
[var]
(let [result (System/getenv var)]
(if (nil? result)
(throw (Exception. (str "Environment variable: " var " not set. Aborting")))
result)))
(def access_key
(let [user (get-env-var "SUMO_ID")
pass (get-env-var "SUMO_KEY")]
[user pass]))
(defn respond
[]
(let [{:keys [status body error] :as response} (http/get endpoint rest-options)]
(if error
(println error)
(print-response body))))
When I run the program using leiningen as lein run -- -? or even just lein run, I get this error, even though I haven't explicitly called the sumologic function. What am I doing wrong and what are things that I can do differently?
Caused by: java.lang.Exception: Environment variable: SUMO_KEY not set. Aborting
at clarion.sumo$get_env_var.invoke(sumo.clj:14)
at clarion.sumo$fn__3765.invoke(sumo.clj:19)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3553)
You have def'd access_key so it is being evaluated when you load the application. You probably want to make it a function instead.

can't connect my compojure app to postgres db (following heroku tutorial)

i'm following along the heroku compojure tutorial. When I get to the point in the tutorial where a table is created, I get the following error message:
user=> (require '[clojure.java.jdbc :as sql])
nil
user=> (sql/with-connection (System/getenv "DATABASE_URL")
#_=> (sql/create-table :testing [:data :text]))
IllegalArgumentException db-spec postgresql://localhost:5432/lord is missing a required parameter clojure.java.jdbc.internal/get-connection (internal.clj:147)
I've tried adding the name of the linux user i created the db with and that users's password
to the DATABASE_URL but no luck. I'm missing something here and i'm not sure what it is. Where is db-spec defined might be the right question but i'm not exactly sure.
Try putting the connection into a map like this (this is mysql specific but you get the gist):
(def db-conn {
:classname "com.mysql.jdbc.Driver" ;must be in classpath
:subprotocol "mysql"
:subname (str "//" DBHOST ":" DBPORT "/" db-name)
; Any additional keys are passed to the driver as driver-specific properties.
:user DBUSER
:password DBPASS})
(sql/with-connection db-conn
(sql/create-table :testing [:data :text]))))
Hope this helps.

What is the proper way to store a global connection in Clojure?

I have the following file that acts as the access point to my DB from my API endpoints. What is the proper way to maintain a single connection (or a connection pool?) for the life of the server?
(ns my-api.repository
(:require [clojure.java.jdbc :as sql]))
(defn some-query
(sql/with-connection (System/getenv "DATABASE_URL")
(sql/with-query-results results
;; You get the picture
)))
the DATABASE_URL is stored, if you use with-connection macro for single connection. you can use c3p0 library for connection pool:
(defn pooled-spec
"return pooled conn spec.
Usage:
(def pooled-db (pooled-spec db-spec))
(with-connection pooled-db ...)"
[{:keys [classname subprotocol subname user password] :as other-spec}]
(let [cpds (doto (ComboPooledDataSource.)
(.setDriverClass classname)
(.setJdbcUrl (str "jdbc:" subprotocol ":" subname))
(.setUser user)
(.setPassword password))]
{:datasource cpds}))
I also recommend c3p0.
Clojure connection pooling
provides a succinct introduction to how to configure clojure.java.jdbc with c3p0.
jdbc-pool simplifies the process of using C3P0 with clojure.java.jdbc. I created the library specifically (and only) for this purpose.
I do not think with-connection holds anything open. Doc nor source suggest it, and in 2.3 I was able to confirm it by inspecting db after running queries. The source looks like this:
(defn with-connection*
"Evaluates func in the context of a new connection to a database then
closes the connection."
[db-spec func]
(with-open [^java.sql.Connection con (get-connection db-spec)]
(binding [*db* (assoc *db* :connection con :level 0 :rollback (atom false))]
(func))))
Connection pooling may be useful in lazily creating them, but that doesn't hold them open. Making the connection settable seems to be necessary. However the latest API emphasizes just creating the connection and passing it into each call. While still ALPHA, this looks like the future for this library (and is still compatible with connection pooling). From the library's wiki:
(require '[clojure.java.jdbc :as j]
'[clojure.java.jdbc.sql :as s])
(def mysql-db {:subprotocol "mysql"
:subname "//127.0.0.1:3306/clojure_test"
:user "clojure_test"
:password "clojure_test"})
(j/insert! mysql-db :fruit
{:name "Apple" :appearance "rosy" :cost 24}
{:name "Orange" :appearance "round" :cost 49})