2

I'd like to create one jar file to execute that is implemented with java and clojure. This is the step that I took.

Making my java code call clojure code

I could generate a jar file (ThingOne-1.0.0-SNAPSHOT-standalone.jar) that has clojure core and my clojure code, and I also could get a class file (HelloJava.class) that uses the clojure code in the jar file by following this site - http://walkwithoutrhythm.net/blog/2012/03/26/how-to-call-clojure-1-dot-3-functions-from-java/

The java code is as follows: clojure code is imported as ThingOne

import ThingOne.*;

public class HelloJava {
    public static void main(String[] args) {
        System.out.println("Hello from Java!");
        core.foo (12345);
    }
}

I can run this command to use the code:

java -cp 'ThingOne-1.0.0-SNAPSHOT-standalone.jar:.' HelloJava

Making one jar file

I made one jar directory that has this structure.

├── MANIFEST.MF
└── jar
    └── ThingOne-1.0.0-SNAPSHOT-standalone.jar

The content of MANIFEST.MF is

Manifest-Version: 0.1
Main-Class: HelloJava
Class-Path: jar/ThingOne-1.0.0-SNAPSHOT-standalone.jar

I could get one jar file with jar cvfm hello.jar jar/MANIFEST.MF HelloJava.class.

However, the clojure jar file (ThingOne-1.0.0-SNAPSHOT-standalone.jar) is not included in this jar file, but just referenced.

How can I make one jar file that contains the java class file and clojure jar file?

prosseek
  • 182,215
  • 215
  • 566
  • 871
  • are you using maven? there is an option for maven plugin to set the main class. – gigadot Sep 09 '12 at 18:59
  • @gigadot: No, I'm not using maven. – prosseek Sep 09 '12 at 19:01
  • 3
    are you using Leiningen? – georgek Sep 09 '12 at 19:33
  • I don't understand the content of your manifest file; isn't a manifest file name-value pairs? Also, it must end with a newline; make sure you have that. – Dave Newton Sep 09 '12 at 21:06
  • You say you "added" `Main-Class: HelloJava` to the manifest (I can't imagine `java -jar` worked if there was no line like that in the manifest). Delete the other lines if they're still there. Also have a look at the `-m` option to `jar`. And embedding a jar inside another jar won't work like that, keep the `ThingOne*.jar` on the same level as the `hello.jar`. – martijno Sep 09 '12 at 21:09
  • 3
    Do you really need the Java file? It seems like it'd be a lot easier to follow the steps in _[Creating Standalone Java Applications with Leiningen](http://asymmetrical-view.com/2010/06/08/building-standalone-jars-wtih-leiningen.html)_, which will create a runnable JAR file that just calls the `-main` function in the specified Clojure-generated class. – DaoWen Sep 09 '12 at 22:18
  • @Dave Newton: The manifest content was typo, I updated the new one. – prosseek Sep 09 '12 at 22:47
  • What are those things after the Main-Class supposed to be, classpath entries? Also, I'd add the manifest version header--I don't know if it's required or not, though. – Dave Newton Sep 09 '12 at 22:53
  • 1
    Also, for inspiration, take a look at a GitHub project, [atea](https://github.com/pkamenarsky/atea) which uses Clojure to make a desktop-like tray application. – Nick Klauer Sep 11 '12 at 15:00

2 Answers2

1

If you want to make an all-inclusive jar, use tools like:

If you specifically want to package jar files, you need a custom classloader, like:

That said, I'd consider Dao Wen's suggestion, unless you have a specific need it cannot meet.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • I tried one-jar to create the jar file, but it doesn't execute with runtime error. - http://stackoverflow.com/questions/12344459/using-one-jar-to-build-one-jar-file – prosseek Sep 10 '12 at 01:41
  • I think that comment by DaoWen answers your question in full. Please read through Creating Standalone Java Applications with Leiningen that he linked to. Installing [Leiningen](http://leiningen.org/) is quite easy: download the script and run it. Here's a [sample project.clj](https://github.com/technomancy/leiningen/blob/master/sample.project.clj) file that lists all possible options. – Ivan Koblik Sep 10 '12 at 07:42
  • @Ivan: The clojure code is called as a API from Java code. Clojure is very small part of the whole program. I'm not sure if Leiningen works for this case. – prosseek Sep 11 '12 at 15:35
  • @prosseek In this case, would it make sense to create a Clojure library that your project depends upon? Alternatively Leiningen can handle Java-only projects. Please take a look at sample project.clj file, search for :java-source-paths. **Question:** how do you bootstrap Clojure? – Ivan Koblik Sep 11 '12 at 15:43
  • @Ivan: As is described in the post (or in the linked page), I could use Leiningen to create one uber jar file, and Java calls the API in the uber jar file as a API. Though I'm not sure this answers your 'Bootstrapping Clojure' question. – prosseek Sep 11 '12 at 16:08
1

As far as I understand your use case is as follows: you have a predominately Java application and you want to make use of some Clojure functions from Java. The best way to do it would be to create a :gen-class which will serve as a Facade to your Clojure functionality. For more information please read this post.

You'd need to create a separate Clojure project which will be managed by Leiningen. Result of the project you can publish to your local mavan repository (Leiningen is a facade to Maven).

In Java project add dependency to your Clojure library, you don't have to worry about clojure.jar as Maven will take care of it.

This is a standard way of developing in Java, please don't crate one monolith Jar with Clojure language and your library in it. If you use Leiningen (Maven) you won't need it.

Alternative: I know of another way to structure your project, when Clojure and Java sources are kept together. It's dirtier but it works. If you'd like me to describe it, please let me know.

EDIT: If you configure your project this way it will be always possible to create uberjar of it. Maven has a plugin for that.

Community
  • 1
  • 1
Ivan Koblik
  • 4,285
  • 1
  • 30
  • 33