Clojure

Jar within a jar throws a runtime error

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: Release 1.2, Release 1.3
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Environment:
    Maven using the one-jar plugin
  • Patch:
    Code
  • Approval:
    Triaged

Description

I've created two jar files in my multi-project Maven setup. The first jar is the "engine", and it includes the clojure jar in it. The other jar is the "application". It includes the engine and then packages itself into a one-jar jar file. This means we have a jar within a jar: The "onejar" contains the engine jar, which in turn contains that clojure jar.

I then get an error in the runtime:

Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.simontuffs.onejar.Boot.run(Boot.java:340)
at com.simontuffs.onejar.Boot.main(Boot.java:166)
Caused by: java.lang.ExceptionInInitializerError
at com.ziroby.clojure.App.main(App.java:14)
... 6 more
Caused by: java.lang.NullPointerException
at clojure.lang.RT.lastModified(RT.java:374)
at clojure.lang.RT.load(RT.java:408)
at clojure.lang.RT.load(RT.java:398)
at clojure.lang.RT.doInit(RT.java:434)
at clojure.lang.RT.<clinit>(RT.java:316)
... 7 more

See also my Stack Overflow question on this at http://stackoverflow.com/questions/7763480/making-an-executable-jar-that-evals-clojure-strings

In researching it, I've found the problem lies in RT.lastModified, where it tries to determine last modified time by looking at the modified time on the jar file for Clojure. But there's not actually a jar file, since it's embedded in another.

I've found that adding a null check solves the problem. My lastModified looks like this now:

static public long lastModified(URL url, String libfile) throws Exception{
if(url.getProtocol().equals("jar")) { ZipEntry entry = ((JarURLConnection) url.openConnection()).getJarFile().getEntry(libfile); if (entry != null) return entry.getTime(); }

return url.openConnection().getLastModified();
}

This runs successfully.

If you'd prefer, I can submit a patch, or commit directly.

  1. clj-971-1.patch
    24/Nov/13 7:11 AM
    1 kB
    Paavo Parkkinen
  2. clj-971-2.patch
    25/Nov/13 6:19 AM
    2 kB
    Paavo Parkkinen

Activity

Hide
Anders Sveen added a comment -

Would be awesome if you could get this working. Wanting to use som Clojure libs in Java so Leiningen uberjar is not an option right now.

Show
Anders Sveen added a comment - Would be awesome if you could get this working. Wanting to use som Clojure libs in Java so Leiningen uberjar is not an option right now.
Hide
Paavo Parkkinen added a comment -

Took the code change in the description and rolled it into a patch to hopefully push this forward a little bit.

Show
Paavo Parkkinen added a comment - Took the code change in the description and rolled it into a patch to hopefully push this forward a little bit.
Hide
Alex Miller added a comment -

Thanks Paavo - on a quick scan, it looks like in the case you're interested in the updated code would now call url.openConnection() twice - perhaps that could be factored out?

Show
Alex Miller added a comment - Thanks Paavo - on a quick scan, it looks like in the case you're interested in the updated code would now call url.openConnection() twice - perhaps that could be factored out?
Hide
Paavo Parkkinen added a comment -

New patch with duplicate calls to url.openConnection() factored out.

Show
Paavo Parkkinen added a comment - New patch with duplicate calls to url.openConnection() factored out.
Hide
Gleb Kanterov added a comment -

I had the same issue, adding following line to manifest file worked for me

One-Jar-URL-Factory: com.simontuffs.onejar.JarClassLoader$OneJarURLFactory
Show
Gleb Kanterov added a comment - I had the same issue, adding following line to manifest file worked for me
One-Jar-URL-Factory: com.simontuffs.onejar.JarClassLoader$OneJarURLFactory
Hide
Stuart Halloway added a comment -

Can we get a test showing the normal path and the can't-read-inside path?

Show
Stuart Halloway added a comment - Can we get a test showing the normal path and the can't-read-inside path?
Hide
Sean Shubin added a comment - - edited

I notice this solution falls back on the last modified date for the entire jar, while it is still possible to get the date of the individual file, albeit less efficiently.
I posted a way to get the individual file date in CLJ-1405.
Perhaps you don't need the date of the individual file, but I am having a hard time groking the calling code so I am not sure what its intent is.
I did notice in the calling code that the if statement
(classURL != null && (cljURL == null || lastModified(classURL, classfile) > lastModified(cljURL, cljfile))) || classURL == null
Is logically equivalent to the much simpler
classURL == null || cljURL == null || lastModified(classURL, classfile) > lastModified(cljURL, cljfile)

Show
Sean Shubin added a comment - - edited I notice this solution falls back on the last modified date for the entire jar, while it is still possible to get the date of the individual file, albeit less efficiently. I posted a way to get the individual file date in CLJ-1405. Perhaps you don't need the date of the individual file, but I am having a hard time groking the calling code so I am not sure what its intent is. I did notice in the calling code that the if statement (classURL != null && (cljURL == null || lastModified(classURL, classfile) > lastModified(cljURL, cljfile))) || classURL == null Is logically equivalent to the much simpler classURL == null || cljURL == null || lastModified(classURL, classfile) > lastModified(cljURL, cljfile)

People

Vote (1)
Watch (2)

Dates

  • Created:
    Updated: