aspose file tools*
The moose likes Clojure and the fly likes Calling Clojure from Java code Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Languages » Clojure
Bookmark "Calling Clojure from Java code" Watch "Calling Clojure from Java code" New topic
Author

Calling Clojure from Java code

Gary Deer
Greenhorn

Joined: Aug 20, 2010
Posts: 26

I have a small Java program that I'm trying to rewrite in Clojure little by little. The first piece I wanted to convert was my logging. I used log4j and I saw a blog post about writing logging in Clojure with log4j, but I didn't see anything about using that for logging in the Java code.

But before I did all that I wanted to see if I could write a simple Clojure file that had functions I could call from a Java program. It was not as easy as I thought it would be. First there were the form considerations (gen-class deftype, defrecord, etc.) then the namespace issues, then aot fun, and after all that, the java classes won't compile. I also tried using leiningen which only made it worse somehow.

With Java interoperability there is a lot to consider. Like if I were to use gen-class for parts to maintain interop with Java parts and then I rewrite those parts in Clojure, will I then need to go back and rewrite the whole thing removing all the interop mess?

I'm in a weird position of trying to create a practical example from a trivial program. But, when I tell people that Clojure has great interoperability with Java, I would like to show them an example of Clojure calling Java and Java Calling Clojure in harmony. Even better if I could give someone a jar that is Clojure files packaged by leiningen that they can use without even knowing it was all written in Clojure.

I'm hoping that the more I play around with it, the more it will click, but as of right now I feel like I'm shaving a yak.


When all you have is a hammer, everything looks like a thumb.
Sean Corfield
Ranch Hand

Joined: Feb 09, 2011
Posts: 261
    
    5

Hard to tell what's going wrong from your description but it should be straightforward.

Basic interop, creating a .class file from Clojure code that can be linked into Java code, should just be gen-class and lien compile.

Happy to help you offlist via screen sharing or whatever...

About the first interop I did with Java calling Clojure was a log4j appended (which I thought I blogged in some detail but I'm on my tablet right now so I'd have to go search for that).
Sean Corfield
Ranch Hand

Joined: Feb 09, 2011
Posts: 261
    
    5

Here's the more detailed blog post: http://corfield.org/blog/post.cfm/real-world-clojure-logging

It links back to the overview post. Was it one of those posts that started you down this path?
Sean Corfield
Ranch Hand

Joined: Feb 09, 2011
Posts: 261
    
    5

OK, here's what I have...

lein new greet

That creates a new Clojure project with src/greet/core.clj and project.clj. Edit the latter to look like this:

Basically just add the :aot line.

Now edit core.clj to look like this:

We specify :gen-class to define what Java class to produce:
:name specifies that we'll generate Greeting.class
:init specifies the constructor function
:constructors specifies that the signature [String] will have a superclass constructor signature of [] - we only have one constructor arity
:methods specifies the signature of methods to generate, in this case just one method, greet, which takes a single String argument and returns a String
:state specifies the name of the accessor function for the state of this object

Then we define the constructor -create which takes a single String argument and returns a pair of: arguments for the super constructor (an empty list) and the state for this object (just the string passed in).

Then we define the -greet method (taking the implicit 'this' argument and our String argument 'n') which returns a greeting string.

At this point we can compile the Clojure to a Java .class file: lein compile

target\classes should contain Greeting.class at this point.

Now, onto the Java code to test this. Create GreetTest.java in the top-level project folder:

Compile it: javac -cp target\classes GreetTest.java

You'll need the Clojure JAR file somewhere easily accessible - I copied it from the local Maven repo (~/.m2/repository/org/clojure/clojure/1.4.0/clojure-1.4.0.jar) to a locally-created lib folder just to make this easier.

Now run the main class: java -cp .;target\classes;lib\clojure-1.4.0.jar GreetTest

You should see: Hey Sean!

Hope that helps?

Now, yes, creating a standalone class like this is quite a bit of work. Life is much easier if you're using Clojure to implement existing interfaces or extend existing classes (because you don't have to specify methods, you might not need to specify the state and/or you might not need to specify the constructors, depending on the base interface/class).

I've just repro'd all of this on Windows 8 hence the \ separators and ; in the classpath. Hope that isn't too confusing if you're on Mac / Linux.
Gary Deer
Greenhorn

Joined: Aug 20, 2010
Posts: 26

Yes, got this to work, thanks.

Using gen-class takes some getting used to, but once I had a working example to play with I started to get it.

I'll try to build off of this and post any interesting discoveries. I'm also going to work on some sort of automation for the build, should be a short shell script.

Thanks for your help.
Sean Corfield
Ranch Hand

Joined: Feb 09, 2011
Posts: 261
    
    5

Gary Deer wrote:Yes, got this to work, thanks.

No problem. Like I say, happy to help one-on-one if you need - just contact me off-list.
Gary Deer wrote:Using gen-class takes some getting used to, but once I had a working example to play with I started to get it.

Yes, it needs to be flexible enough to provide all the metadata for all the ridiculous boilerplate stuff that Java makes us type
Gary Deer wrote:I'll try to build off of this and post any interesting discoveries. I'm also going to work on some sort of automation for the build, should be a short shell script.

Consider using lein javac to compile your Java code too (you'll need to specify :java-source-paths as a vector of directory names in project.clj). That should make it easy to manage compilation of the Clojure code and Java code together, as well as automating generation of a standalone JAR containing the Java plus to Clojure all compiled and ready to run.
guillermo ithier
Greenhorn

Joined: Oct 27, 2013
Posts: 1
Gary Deer wrote:I have a small Java program that I'm trying to rewrite in Clojure little by little. The first piece I wanted to convert was my logging. I used log4j and I saw a blog post about writing logging in Clojure with log4j, but I didn't see anything about using that for logging in the Java code.

But before I did all that I wanted to see if I could write a simple Clojure file that had functions I could call from a Java program. It was not as easy as I thought it would be. First there were the form considerations (gen-class deftype, defrecord, etc.) then the namespace issues, then aot fun, and after all that, the java classes won't compile. I also tried using leiningen which only made it worse somehow.

With Java interoperability there is a lot to consider. Like if I were to use gen-class for parts to maintain interop with Java parts and then I rewrite those parts in Clojure, will I then need to go back and rewrite the whole thing removing all the interop mess?

I'm in a weird position of trying to create a practical example from a trivial program. But, when I tell people that Clojure has great interoperability with Java, I would like to show them an example of Clojure calling Java and Java Calling Clojure in harmony. Even better if I could give someone a jar that is Clojure files packaged by leiningen that they can use without even knowing it was all written in Clojure.

I'm hoping that the more I play around with it, the more it will click, but as of right now I feel like I'm shaving a yak.

Hello all, This is my first time scripting with clojure. i am using eclipse and when irun the code above, I keep getting greet not recognized. what can I do? Thankyou.
Sean Corfield
Ranch Hand

Joined: Feb 09, 2011
Posts: 261
    
    5

Guillermo, you might want to start a new thread for that - and provide a LOT more detail about exactly what you did and exactly what the error message is that you're seeing...
 
wood burning stoves
 
subject: Calling Clojure from Java code