Following the explanation about :gen-class in The Anatomy of gen-class, I used leiningen to get the class files.
leon new pinger to create a project.
cd src and mkdir some and created a Example.clj file in it.
Added :aot [some.Example] (or :aot :all) in project.clj.
The Example.clj is as follows:
(ns some.Example
(:gen-class))
(defn -toString
[this]
"Hello, World!")
Then I executed lein compile to get the classes in target directory.
Then, I was executing this code with lein repl.
(-toString (some.Example.)) ; should return "Hello, World!"
However, I got this error message.
CompilerException java.lang.RuntimeException: Unable to resolve symbol:
-toString in this context, compiling:(/private/var/folders/nw/dmb7jh3d2hq89296z2gnntqm0000gn/T/form-
init7145760420048735997.clj:1:1)
.toString works fine.
user=> (.toString (some.Example.))
"Hello, World!"
The website explains that I should get the same results from both -toString and .toString, but I got only correct results with .toString.
What's the difference between -toString and .toString in Clojure? Why -toString raises an error in the example?
First, some terminology:
(.toString (some.Example.)) is a call to the toString method of the newly constructed some.Example instance.
(-toString (some.Example.)) is a regular Clojure function call, with -toString being the name of a Clojure Var storing a function and (some.Example.) being its sole argument.
:gen-class arranges things so that the -toString Var backs the toString method; that is, any call to the toString method of a some.Example instance results in a call to -toString. So it is indeed the case that just calling -toString directly is equivalent.
However, before you can call a Clojure function by referring to the Var in which it's stored, you need to make sure the namespace in which this Var lives has been loaded (not a problem here, given that you were able to construct an instance of some.Example) and then either refer to the Var by its fully-qualified name, or else use refer, use, require or alias to make it possible to refer to it by a shorter name:
(some.Example/-toString ...)
(use '[some.Example :only [-toString]])
(-toString ...)
(require '[some.Example :refer [-toString]])
(-toString ...)
(require '[some.Example :as se])
(se/-toString ...)
;; refer and alias are typically not used directly
If you say -toString without first using refer, use or require as shown above1, Clojure will attempt to resolve the symbol -toString to a Var in the current namespace (typically user in REPL sessions; with lein repl it may be the :main namespace from your defproject form).
1 That's speaking about the REPL. In a source file, you'd typically use :use or :require in your ns form; the syntax is the same as for use / require minus the quoting.
Related
I am wrapping a java library in Clojure. Depending on the java library version, some classes exist or not, so my library can fail to even compile if it can't find the java classes. My idea was to use Reflector to use the string name of classes.
Example of what I'm trying to do:
(java.time.LocalDateTime/parse "2020-01-01")
would become
(if right-version?
(clojure.lang.Reflector/invokeStaticMethod "java.time.LocalDate" "parse" (into-array ["2020-01-01"]))
This works but is slower by a factor of 20x. Is there a better way to achieve the same? Can I use a macro that will define the correct function at compile time, depending on the version of the underlying library?
Thanks,
I have been using a macro solution to this problem for 6+ years in the Tupelo Library. It allows you to write code like:
(defn base64-encoder []
(if-java-1-8-plus
(java.util.Base64/getEncoder)
(throw (RuntimeException. "Unimplemented prior to Java 1.8: "))))
The macro itself is quite simple:
(defmacro if-java-1-11-plus
"If JVM is Java 1.11 or higher, evaluates if-form into code. Otherwise, evaluates else-form."
[if-form else-form]
(if (is-java-11-plus?)
`(do ~if-form)
`(do ~else-form)))
(defmacro when-java-1-11-plus
"If JVM is Java 1.11 or higher, evaluates forms into code. Otherwise, elide forms."
[& forms]
(when (is-java-11-plus?)
`(do ~#forms)))
and the version testing functions look like
;-----------------------------------------------------------------------------
; Java version stuff
(s/defn version-str->semantic-vec :- [s/Int]
"Returns the java version as a semantic vector of integers, like `11.0.17` => [11 0 17]"
[s :- s/Str]
(let [v1 (str/trim s)
v2 (xsecond (re-matches #"([.0-9]+).*" v1)) ; remove any suffix like on `1.8.0-b097` or `1.8.0_234`
v3 (str/split v2 #"\.")
v4 (mapv #(Integer/parseInt %) v3)]
v4))
(s/defn java-version-str :- s/Str
[] (System/getProperty "java.version"))
(s/defn java-version-semantic :- [s/Int]
[] (version-str->semantic-vec (java-version-str)))
(s/defn java-version-min? :- s/Bool
"Returns true if Java version is at least as great as supplied string.
Sort is by lexicographic (alphabetic) order."
[tgt-version-str :- s/Str]
(let [tgt-version-vec (version-str->semantic-vec tgt-version-str)
actual-version-vec (java-version-semantic)
result (increasing-or-equal? tgt-version-vec actual-version-vec)]
result))
(when-not (java-version-min? "1.7")
(throw (ex-info "Must have at least Java 1.7" {:java-version (java-version-str)})))
(defn is-java-8-plus? [] (java-version-min? "1.8")) ; ***** NOTE: version string is still `1.8` *****
(defn is-java-11-plus? [] (java-version-min? "11"))
(defn is-java-17-plus? [] (java-version-min? "17"))
The advantage of using the macro version is that you can refer to a Java class normally via the symbol java.util.Base64. Without macros, this will crash the compiler for older versions of Java even if wrapped by an if or when, since the symbol will be unresolved before the if or when is evaluated.
Since Java doesn't have macros, the only workaround in that case is to use the string "java.util.Base64"
and then Class/forName, etc, which is awkward & ugly. Since Clojure has macros, we can take advantage of conditional code compilation to avoid needing the powerful (but awkward) Java Reflection API.
Instead of copying or re-writing these functions into your own code, just use put
[tupelo "22.05.04"]
into your project.clj and away you go!
P.S.
You do not need to throw an exception if you detect an older version of Java. This example simply elides the code if the Java version is too old:
(t/when-java-1-11-plus
(dotest
(throws-not? (Instant/parse "2019-02-14T02:03:04.334Z"))
(throws-not? (Instant/parse "2019-02-14T02:03:04Z"))
(throws-not? (Instant/parse "0019-02-14T02:03:04Z")) ; can handle really old dates w/o throwing
...)
I could not tell from your question if your code uses Reflector on every call to the parse method. If so, you could instead define the Method once to use later many times:
(def right-version? true) ; set as appropriate
(def ^java.lang.reflect.Method parse-method
(when right-version?
(.getMethod (Class/forName "java.time.LocalDateTime")
"parse"
(into-array [java.lang.CharSequence]))))
(defn parse-local-date-time [s]
(when parse-method
(.invoke parse-method nil (into-array [s]))))
(parse-local-date-time "2020-01-01T14:30:00")
;; => #object[java.time.LocalDateTime 0x268fc120 "2020-01-01T14:30"]
I need to write an application for a client that calls a method from a ".dll" file. The ".dll" file was previously executed manually from an ".exe" GUI but now they want to automate the process.
I never worked with .dll files so everything that I found until now is the result of a complete day of research, I also received a small documentation with this tool:
The interface is an ActiveX DLL which provides two functions (GetUnitInfo and SaveResult).
In the moment I just want to run the "GetUnitInfo" method from the Winwdows command line using RUNDLL32.exe.
This is the documentation for the "GetUnitInfo" method:
The interface for GetUnitInfo is as follows:
Public Function GetUnitInfo( _
ByVal strRequest As String, _
ByRef strUnitInfo As String,
Optional ByVal strStationName As String = "") As Long
Sample calling code can be:
Dim lRet As Long
Dim strXML as String
lRet = GetUnitInfo( _“<?xml version=""1.0"" ?><GetUnitInfo
xmlns=""urn:GetUnitInfo-schema"" SerialNumber=""BD3ZZTC8MA"" />", strXML)
So I tried to run this method with some dummy parameters because the method returns an error if the parameters are not OK. The command:
RUNDLL32.EXE FFTester.dll, GetUnitInfo test1, test2
But I receive this error:
I used "Dependency Walker" to list the functions from the dll file:
But this are all the functions, normally I would expected that also "GetUnitInfo" is listed.
Can somebody help? It is not mandatory to use RUNDLL32.
Later edit:
I want to call this DLL from a tool that is written in JAVA, I tried to use JNA but I failed so I was thinking to call the dll functions from the command line because if this works I can use a process builder to execute the command.
I fixed my problem and I will provide a solution, maybe it will help someone else.
I used com4j library to generate the interfaces for my dll. After this you need to register your DLL otherwise most problely your code will throw an "ComException", you can read more in my second question.
To register a DLL:
C:\Windows\SysWOW64>regsvr32.exe "path to your DLL" for 32 bit DLL
Or
C:\Windows\System32>regsvr32.exe "path to your DLL" for 64 bit DLL
Also depending on your DLL type, 32 or 64 bit, you need to use proper Eclipse/JDK.
I just started learning about groovy and trying to transpose my java code to groovy scripts. Usually java allows you have a class with only methods that you can call from other classes. I wanted to translate that to groovy. I have in one file - lets call it File1- a method like this:
def retrieveData(String name){
// do something
}
and in the second file, File2, I call File1 like this:
def file1Class = this.class.classLoader.parseClass(new File("../File1.groovy"))
and then try to call the method in File1 like this:
def data = file1Class.retrieveData("String")
but it keeps giving me this error - MissingMethodException:
groovy.lang.MissingMethodException: No signature of method: static File1.retrieveData() is applicable for argument types: (java.lang.String) values: [String] Possible solutions: retrieveData(java.lang.String)
so it does recognize that I am sending in the correct number of parameters and even the correct object, but it isn't running the method as it should?
Is there something I am missing? I tried to remove the object definition from the method - in other words - like this:
def retrieveData(name){
// do something
}
but that didn't work either. I am clueless about what the next step would be. Can anyone please help push me in the right direction? I would greatly appreciate it.
See the answer provided in this StackOverflow reponse.
Use the GroovyScriptEngine class. What does the GroovyScriptEngine do? From the docs:
Specific script engine able to reload modified scripts as well as
dealing properly with dependent scripts.
See the example below.
def script = new GroovyScriptEngine( '.' ).with {
loadScriptByName( '..\File1.groovy' )
}
this.metaClass.mixin script
retrieveData()
Note how we use the loadScriptByNamemethod to
Get the class of the scriptName in question, so that you can
instantiate Groovy objects with caching and reloading.
This will allow you to access Groovy objects from files however you please.
I have a Groovy file that looks like this (currently).
main.groovy
import org.packages.mystuff.JavaClassIAmUsing;
public class MyObject {
def rate(item){
def o = evaluate(new File (new File(getClass().protectionDomain.codeSource.location.path).parent),"CommonFunctions.groovy");
println o.whoami();
}
}
i have another groovy file called
CommonFunctions.groovy
def whoami() {return 'no body';}
I'm trying to include the CommonFunctions script in to main script, BUT the location of the script are not known at build time (i.e. i can not hardcode a absolute file path in the script or absoulte path of the java process in relation to where the scripts will be stored).
All i know is that the scripts will be together or at a location relative to the calling script (say sub directory).
I've attempted to try and location the calling script location, but i get the error
No signature of method: MyObject.evaluate()
How can i referance this script, considering the main script is accessed at runtime using a GroovyClassLoader.parseClass(File) method.
I'm not really sure why you want to do it this way, I think it would be much simpler to make a class of CommonsFunctions that you could instantiate normally and use everywhere.
However, it is possible to achieve what you want; with Groovy, there are not that many limitations...
There are two problems with your suggested solution:
getClass() inside your MyObject class naturally refers to ... the MyObject class, so your attempt to find the location of the script will fail. You're on the right track, but you need to resolve the script location using the surrounding Script class.
evaluate doesn't really work the way you think it does. The result of the evaluate method is the result of the script, not an instance of the Script class. One way to remedy this, is to rewrite the methods in CommonFunction as closure properties. These properties will be available in the shell Binding object when evaluating the script.
So, with these rewrites, you end up with something like this:
main.groovy
class MyObject {
def scriptDir
def rate(item) {
def commonFunctionsScriptFile = new File(scriptDir, "CommonFunctions.groovy")
def binding = new Binding()
new GroovyShell(binding).evaluate(commonFunctionsScriptFile)
println binding.variables.whoami()
}
}
scriptFile = new File(getClass().protectionDomain.codeSource.location.path)
new MyObject(scriptDir: scriptFile.parentFile).rate(null)
Here, the script file location is resolved in the script, not in the inner class.
CommonFunctions.groovy
whoami = { 'no body' }
Here, whoami is no longer a method, but a closure property which will be added to the binding. Make sure that you don't prefix this property with def, since then it will be a local variable instead of a property added to the binding object.
Output after these rewrites is the expected: no body.
I'm trying to use boilerpipe from JRuby. I've seen the guide for calling Java from JRuby, and have used it successfully with another Java package, but can't figure out why the same thing isn't working with boilerpipe.
I'm trying to basically do the equivalent of this Java from JRuby:
URL url = new URL("http://www.example.com/some-location/index.html");
String text = ArticleExtractor.INSTANCE.getText(url);
Tried this in JRuby:
require 'java'
url = java.net.URL.new("http://www.example.com/some-location/index.html")
text = Java::DeL3sBoilerpipeExtractors::ArticleExtractor.INSTANCE.getText(url)
This is based on the API Javadocs for boilerpipe. Here's the error:
jruby-1.6.0 :042 > Java::DeL3sBoilerpipeExtractors::ArticleExtractor
NameError: cannot load Java class deL3sBoilerpipeExtractors.ArticleExtractor
from org/jruby/javasupport/JavaClass.java:1195:in `for_name'
from org/jruby/javasupport/JavaUtilities.java:34:in `get_proxy_class'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/site_ruby/shared/builtin/javasupport/java.rb:45:in `const_missing'
from (irb):42:in `evaluate'
from org/jruby/RubyKernel.java:1087:in `eval'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:158:in `eval_input'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:271:in `signal_status'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:270:in `signal_status'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:155:in `eval_input'
from org/jruby/RubyKernel.java:1417:in `loop'
from org/jruby/RubyKernel.java:1190:in `catch'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:154:in `eval_input'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:71:in `start'
from org/jruby/RubyKernel.java:1190:in `catch'
from /usr/local/rvm/rubies/jruby-1.6.0/lib/ruby/1.8/irb.rb:70:in `start'
from /usr/local/rvm/rubies/jruby-1.6.0/bin/irb:17:in `(root)'
Looks like it didn't parse the camelcase into the appropriate Java package name. What am I doing wrong? I believe I've set up my classpath alright (last 3 entries), though there may be some conflict with xerces possibly being included twice:
$ echo $CLASSPATH
:/jellly/Maui1.2:/jellly/Maui1.2/src:/jellly/Maui1.2/bin:/jellly/Maui1.2/lib/commons-io-1.4.jar:/jellly/Maui1.2/lib/commons-logging.jar:/jellly/Maui1.2/lib/icu4j_3_4.jar:/jellly/Maui1.2/lib/iri.jar:/jellly/Maui1.2/lib/jena.jar:/jellly/Maui1.2/lib/maxent-2.4.0.jar:/jellly/Maui1.2/lib/mysql-connector-java-3.1.13-bin.jar:/jellly/Maui1.2/lib/opennlp-tools-1.3.0.jar:/jellly/Maui1.2/lib/snowball.jar:/jellly/Maui1.2/lib/trove.jar:/jellly/Maui1.2/lib/weka.jar:/jellly/Maui1.2/lib/wikipediaminer1.1.jar:/jellly/Maui1.2/lib/xercesImpl.jar:/jellly/boilerpipe-1.1.0/boilerpipe-1.1.0.jar:/jellly/boilerpipe-1.1.0/lib/nekohtml-1.9.13.jar:/jellly/boilerpipe-1.1.0/lib/xerces-2.9.1.jar
I'd recommend against trying to guess the module name we put under Java::, since for unusual packages it can get mangled pretty badly. Use java_import 'your.weird.package.ArticleExtractor' or if all the package components are compatible with Ruby method naming, you can also do Java::your.weird.package.ArticleExtractor.
Also, since you might run into this... you'll want to reference the INSTANCE variable as ArticleExtractor::INSTANCE, since we map it as a Ruby constant.
Have fun!
You can also use the nice Jruby Boilerpipe Gem which wraps the Java code
Or the pure ruby implementation of Boilerpipe Ruby Boilerpipe Gem
require 'boilerpipe'
Boilerpipe::Extractors::ArticleExtractor.text("https://github.com/jruby/jruby/wiki/AboutJRuby")