Win a copy of Re-engineering Legacy Software this week in the Refactoring forum
or Docker in Action in the Cloud/Virtualization forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Calling Clojure from Java code

 
Gary Deer
Greenhorn
Posts: 26
Android Clojure Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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.
 
Sean Corfield
Ranch Hand
Posts: 302
10
Clojure Linux Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 302
10
Clojure Linux Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 302
10
Clojure Linux Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 26
Android Clojure Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 302
10
Clojure Linux Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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
Posts: 302
10
Clojure Linux Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
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...
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic