I am trying to replicate a snippet of code from the Infinispan documentation in Clojure:
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addStore(SoftIndexFileStoreConfigurationBuilder.class)
.indexLocation("/tmp/sifs/testCache/index");
.dataLocation("/tmp/sifs/testCache/data")
Here is my attempt:
(import '[org.infinispan.persistence.sifs.configuration.SoftIndexFileStoreConfigurationBuilder])
(import '[org.infinispan.configuration.cache ConfigurationBuilder])
(def b (ConfigurationBuilder.))
(.addStore (.persistence b)
(class org.infinispan.persistence.sifs.configuration.SoftIndexFileStoreConfigurationBuilder))
;; java.lang.ClassNotFoundException: org.infinispan.persistence.sifs.configuration.SoftIndexFileStoreConfigurationBuilder
How do I get past this exception? This is the SIFS source
For your case, you should have in your dependencies:
:dependencies ... others here ...
[org.infinispan/infinispan-core "9.0.1.Final"]
[org.infinispan/infinispan-persistence-soft-index "9.0.1.Final"]
Also, you are using import incorrectly. You can either do:
(import full.package.name.ClassNameHere)
or
(import [full.package.name ClassNameOne ClassNameTwo])
For your case, you should be doing:
(import org.infinispan.persistence.sifs.configuration.SoftIndexFileStoreConfigurationBuilder)
Note that unlike require, where a nil result means "everything's good," with import a nil return means "nothing was imported."
Related
I'm using maven with several modules, one in java, another in clojure. I'm calling a clojure function from java and want to pass in a HashMap as a parameter and return a HashMap.
(I ran lein uberjar and lein pom on the clojure project to make it work with maven. I can get things to work for clojure function with simple types e.g. String, so the maven setup does work.)
I am getting the following error when I run some java unit tests calling the java code:
java.lang.ClassCastException: class clojure.lang.LazySeq cannot be cast to class java.util.Map (clojure.lang.LazySeq is in unnamed module o
f loader 'app'; java.util.Map is in module java.base of loader 'bootstrap')
How can I get this to work? Is this the proper way to call clojure methods from java?
What about if the HashMap had a POJO object as a value rather than a String?
My java code:
import interop.Core;
public class BillingCalc {
static Map<String, String> nonEmptyItems(Map<String, String> items) {
return Core.non_empty_seats(new HashMap<String, String>());
}
}
My clojure code:
(ns interop.core
(:gen-class
:name interop.Core
:methods [^{:static true} [apply_vat_to_netto [java.math.BigDecimal java.math.BigDecimal] java.math.BigDecimal]
^{:static true} [non_empty_seats [java.util.Map] java.util.Map]]) )
(defn -filter-empty-seats
"filter out those with empty seats"
[seats]
(filter (fn [[_ v]] (pos? (:booked-items v))) seats))
(defn -non_empty_seats
[java-seats]
(-filter-empty-seats (into {} java-seats)))
I guess that your error is caused by this definition in :gen-class:
[non_empty_seats [java.util.Map] java.util.Map]]
From docs for :gen-class:
:methods [ [name [param-types] return-type], ...]
The expected type of returned value is java.util.Map, but filter in -filter-empty-seats returns instance of clojure.lang.LazySeq. You should rewrite -non_empty_seats like this:
(defn -non_empty_seats
[java-seats]
(into {} (-filter-empty-seats java-seats)))
I am desperetly trying to create a class that with a "toString" method from clojure
According to clojure docs the following should work:
(ns override-test.simpleClass
(:gen-class
:name simpleClass
:methods [[^{Override {}} toString [] String]]
:state state
:init init
:constructors {[String] []}))
(defn -init
[name_]
[[] (atom name_)])
(defn -toString [this]
(deref (.state this)))
However evaluating
(simpleClass. "test")
Throws
CompilerException java.lang.ClassFormatError: Duplicate method name "toString" with signature "()Ljava.lang.String;" in class file simpleClass, compiling:(override_test/simpleClass.clj:19:3)
Any incites of what i might be doing wrong ?
As Biped Phill mentioned the problem was that toString seems to be already implemented by virtue of the automatic subclassing mechanism of gen-class. Probably Ljava.lang. String is treated as a (?) superclass and toString is added automatically, so i just had to remove it from :methods which is for not inherited methods and it worked like a charm.
I am attempting to use the gcloud library.
(ns firengine.state
(:import
[com.google.cloud AuthCredentials]
[com.google.cloud.datastore DatastoreOptions]))
(-> (DatastoreOptions/builder)
(.projectId "<project_id>")
(.authCredentials
(AuthCredentials/createForJson
(clojure.java.io/input-stream service-account-path)))
.build)
The above clojure code is translated from the following code snippet (ellided, click on "Run elsewhere").
import com.google.cloud.AuthCredentials;
import com.google.cloud.datastore.DatastoreOptions;
DatastoreOptions options = DatastoreOptions.builder()
.projectId(PROJECT_ID)
.authCredentials(AuthCredentials.createForJson(
new FileInputStream(PATH_TO_JSON_KEY))).build();
When I call this code from the Clojure REPL, I get the following error.
Unhandled java.lang.IllegalArgumentException
Can't call public method of non-public class: public
com.google.cloud.ServiceOptions$Builder
com.google.cloud.ServiceOptions$Builder.projectId(java.lang.String)
Reflector.java: 88 clojure.lang.Reflector/invokeMatchingMethod
Reflector.java: 28 clojure.lang.Reflector/invokeInstanceMethod
boot.user4590132375374459695.clj: 168 firengine.state/eval17529
boot.user4590132375374459695.clj: 167 firengine.state/eval17529
Compiler.java: 6927 clojure.lang.Compiler/eval
... elided ...
The com.google.cloud.datastore.DatastoreOptions code can be found here.
Updated June 29, 2016:
Pursuant to Schlomi's advice below, I thought that maybe if I AOT compiled my own wrapper around DatastoreOptions that it would work.
(ns firengine.datastore
(:import
[com.google.cloud AuthCredentials]
[com.google.cloud.datastore Datastore DatastoreOptions Entity Key KeyFactory])
(:gen-class
:state state
:init init
:constructors {[String String] []}))
(defn -init
[^String project-id ^String service-account-path]
(let [service-account (clojure.java.io/input-stream service-account-path)
credentials (AuthCredentials/createForJson service-account)
dsoptions (-> (DatastoreOptions/builder)
(.projectId project-id)
(.authCredentials credentials)
.build)]
[[] {:project-id project-id
:service-account-path service-account-path
:datastore-options dsoptions}]))
I modified my boot development task to include the following:
(deftask development
"Launch Immediate Feedback Development Environment"
[]
(comp
(aot :namespace '#{firengine.datastore})
(repl :port 6800)
(reload)
(watch)
(cljs)
(target :dir #{"target"})))
And I attempted to construct the object like so:
(def service-account-path (System/getenv "FIRENGINE_SERVICE_ACCOUNT_PATH"))
(def project-id (System/getenv "PROJECT_ID"))
(def datastore-options (firengine.datastore. project-id service-account-path))
Unfortunately, I still get the same error?
clojure.lang.Compiler$CompilerException: java.lang.reflect.InvocationTargetException, compiling:(state.clj:15:1)
java.lang.reflect.InvocationTargetException:
java.lang.IllegalArgumentException: Can't call public method of non-public class: public com.google.cloud.ServiceOptions$Builder com.google.cloud.ServiceOptions$Builder.projectId(java.lang.String)
Am I not really aot compiling firengine.datastore?
Yeah.... that problem. You wouldnt believe it, but its actually an open bug in the jdk from ... wait for it ... 1999!
You can read about it more in clojure jira and on google groups.
You might have to make your own java wrapper to avoid this, or ask the library author to take this old known java bug into consideration.
If you dont want to write your own java wrapper, and the author insists that "this is the best design, like, ever!", then you could always force it by setting the method accessibility with (.setAccessible method true) and some more custom reflection code..
Good luck!
As Shlomi said, that's a long running bug. Following the advice of Noam Ben Ari on the clj-jira, I've managed the circumvent the issue by writing a small java class that wraps the client creation. I can then use that directly from my clj code.
package pubsub_echo.pubsub;
import com.google.cloud.AuthCredentials;
import com.google.cloud.pubsub.PubSub;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class GCloudPubSub {
public PubSub getClient() throws FileNotFoundException, IOException {
PubSubOptions.Builder optionsBuilder = PubSubOptions.builder();
ClassLoader classLoader = getClass().getClassLoader();
FileInputStream authStream = new FileInputStream(classLoader.getResource("SERVICE_ACCOUNT.json").getPath());
AuthCredentials creds = AuthCredentials.createForJson(authStream);
return optionsBuilder
.authCredentials(creds)
.projectId("PROJECT_ID")
.build()
.service();
}
}
For guidance on adding Java compilation to your project:
https://github.com/technomancy/leiningen/blob/master/doc/MIXED_PROJECTS.md
so, i am a complete Clojure noob and ran into a similar error using Caffeine Cache: "IllegalArgumentException Can't call public method of non-public class: public default void com.github.benmanes.caffeine.cache.LocalManualCache.put(java.lang.Object,java.lang.Object) clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)"
my original function signature was this:
(defn get
[cache key]
(.getIfPresent cache key)
)
and i think the issue is that Clojure could not figure out where to apply Cache's .getIfPresent function. Adding the type fixed it:
(defn get
[^Cache cache key]
(.getIfPresent cache key)
)
Even though it's not a direct answer and your question went over my head, I hope this helps.
Completely inspired by hironroy's answer, I worked through an isolated example of getting this working. Created the file below in src/gcloud/GcloduDatastore.java in the following github project.
package gcloud;
import com.google.cloud.AuthCredentials;
import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.DatastoreOptions.Builder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class GCloudDatastore {
public static Datastore getDatastore() throws FileNotFoundException, IOException {
DatastoreOptions.Builder optionsBuilder = DatastoreOptions.builder();
FileInputStream authStream = new FileInputStream(System.getenv("SERVICE_ACCOUNT_DOT_JSON_PATH"));
AuthCredentials creds = AuthCredentials.createForJson(authStream);
return optionsBuilder
.authCredentials(creds)
.projectId(System.getenv("PROJECT_ID"))
.build()
.service();
}
}
I am using Clojure and Monger
It works fine, and I group functions by the collection they relate to.
Therefore, every file begins like this :
(ns img-cli.model.mycollectionname
(:require [monger.core :as mg]
[monger.collection :as mc]
[edn-config.core :refer [env]])
(:import [com.mongodb MongoOptions ServerAddress DB WriteConcern]
[org.bson.types ObjectId]))
(def config (get-in env [:mongo]))
;; using MongoOptions allows fine-tuning connection parameters,
;; like automatic reconnection (highly recommended for production
;; environment)
(def ^MongoOptions opts (mg/mongo-options { :threads-allowed-to-block-for-connection-multiplier 300}))
(def ^ServerAddress sa (mg/server-address (:url config) (:port config)))
(def conn (mg/connect sa opts))
(def db (mg/get-db conn (:db config)))
(def collection-name "asset")
;; I have to write one like this every time
(defn find-one-as-map
"fetch asset by Id"
[^String id]
(mc/find-one-as-map db collection-name {:_id (ObjectId. id)}))
Code duplication has of course several disadvantages in itself.
Also I'm not sure if connections are properly pooled afterwards ?
How can I avoid doing this ?
I sense I could pass an additional "db" parameter to each function, but then where would it come from ?
If I create the db connection in the "entry" file of my program, then how could it be passed to every function from there ?
For instance let's says I have Compojure routes in different files :
;; in the main handler file
(def db ...) ;; if I move the previous db configuration
;; in here, it could be the only place where this is set
;; importing Compojure routes from different files
(defroutes routes-from-file1
routes-from-file2...)
Let's say that some functions called from some of the routes in "file2" need access to the db, how can I pass this variable to them ?
I also have a lot of repetitive code afterwards, for instance to get data by Id for every collection...
I feel this could be simplified, but I'm not sure how.
Just refer to it by its namespace
(ns foo
(:require [handler :as h]))
(println h/db)
I'm experiencing a strange problem with Java interop. I wrote a small specialized wrapper around the BDB JE API. When I'm in the repl (cider-repl) everything works fine: I can open the database, add entries, etc. But if I call the add-record function from another function in my app I get an NPE,
NullPointerException clojure.lang.Reflector.invokeInstanceMethod (Reflector.java:26)
I'm starting to think I'm losing my mind. This opens the database:
(defn opendb [envpath dbname]
(let [envconf (.setAllowCreate (EnvironmentConfig.) true)
env (Environment. (ensure-directory envpath) envconf)
dbconf (-> (DatabaseConfig.) (.setAllowCreate true) (.setDeferredWrite true))
database (.openDatabase env nil dbname dbconf)]
{:environment env :database database}))
Here is add-record:
(defn add-record [dbm k v]
(let [key (DatabaseEntry. (.getBytes k "UTF-8"))
value (DatabaseEntry. (.getBytes v "UTF-8"))]
(.put (:database dbm) nil key value)))
There really isn't anything special going on here. The usage where this yields the NPE is similar to
(let [cache (bdb/opendb "local-cache" "subject-map")]
;; do stuff
(doseq [node (function "that returns a sequence of maps")]
(bdb/add-record cache (:foo node) (:bar node)))
(bdb/closedb cache))
I've added prnS inside bb/add-record and all the values are non-nil, yet I still get the NPE.
Something subtle is going on here, and it's eluding me. Anyone have an idea?
Thanks inadvance.
EDIT: here's the full stack trace:
Exception in thread "main" java.lang.NullPointerException
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:26)
at sterling.bdbje$add_record.invoke(bdbje.clj:33)
at sterling.loaders$load_subject_batch.invoke(loaders.clj:214)
at sterling.loaders$load_subject_headings.invoke(loaders.clj:224)
at sterling.loaders$_main.doInvoke(loaders.clj:257)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at sterling.loaders.main(Unknown Source)
As I said, this makes no sense to me, but is completely reproducible.