Is it possible to specify a Java classpath that includes a JAR file contained within another JAR file?
If you're trying to create a single jar that contains your application and its required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.
I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).
You do NOT want to use those "explode JAR contents" solutions. They definitely make it harder to see stuff (since everything is exploded at the same level). Furthermore, there could be naming conflicts (should not happen if people use proper packages, but you cannot always control this).
The feature that you want is one of the top 25 Sun RFEs: RFE 4648386, which Sun, in their infinite wisdom, has designated as being of low priority. We can only hope that Sun wakes up...
In the meanwhile, the best solution that I have come across (which I wish that Sun would copy in the JDK) is to use the custom class loader JarClassLoader.
After some research I have found method that doesn't require maven or any 3rd party extension/program.
You can use "Class-Path" in your manifest file.
For example:
Create manifest file MANIFEST.MF
Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass
Compile all your classes and run jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar
c stands for create archive
f indicates that you want to specify file
v is for verbose input
m means that we will pass custom manifest file
Be sure that you included lib in jar package. You should be able to run jar in the normal way.
based on: http://www.ibm.com/developerworks/library/j-5things6/
all other information you need about the class-path do you find here
Use the zipgroupfileset tag (uses same attributes as a fileset tag); it will unzip all files in the directory and add to your new archive file.
More information: http://ant.apache.org/manual/Tasks/zip.html
This is a very useful way to get around the jar-in-a-jar problem -- I know because I have googled this exact StackOverflow question while trying to figure out what to do. If you want to package a jar or a folder of jars into your one built jar with Ant, then forget about all this classpath or third-party plugin stuff, all you gotta do is this (in Ant):
<jar destfile="your.jar" basedir="java/dir">
...
<zipgroupfileset dir="dir/of/jars" />
</jar>
If you are building with ant (I am using ant from eclipse), you can just add the extra jar files
by saying to ant to add them...
Not necessarily the best method if you have a project maintained by multiple people but it works for one person project and is easy.
for example my target that was building the .jar file was:
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
<attribute name="Author" value="ntg"/>
................................
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
I just added one line to make it:
<jar ....">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<manifest>
................................
</manifest>
</jar>
where
<property name="external-lib-dir"
value="C:\...\eclipseWorkspace\Filter\external\...\lib" />
was the dir with the external jars.
And that's it...
Not without writing your own class loader. You can add jars to the jar's classpath, but they must be co-located, not contained in the main jar.
You need to build a custom class-loader to do this or a third-party library that supports this. Your best bet is to extract the jar from the runtime and add them to the classpath (or have them already added to the classpath).
I use maven for my java builds which has a plugin called the maven assembly plugin.
It does what your asking, but like some of the other suggestions describe - essentially exploding all the dependent jars and recombining them into a single jar
If you have eclpise IDE, you just need to export your JAR and choose "Package Required libraries into generated JAR". eclipse will automatically add the required dependant JARs into the generated JAR as well as generated some eclipse custom class loader that load these JARs automatically.
I was about to advise to extract all the files at the same level, then to make a jar out of the result, since the package system should keep them neatly separated.
That would be the manual way, I suppose the tools indicated by Steve will do that nicely.
Winstone is pretty good http://blog.jayway.com/2008/11/28/executable-war-with-winstone-maven-plugin/. But not for complex sites. And that's a shame because all it takes is to include the plugin.
Well, there is a very easy way if you're using Eclipse.
Export your project as a "Runnable" Jar file (right-click project folder from within Eclipse, select "Export..."). When you configure the export settings, be sure to select "Extract required libraries into generated Jar." Keep in mind, select "Extract..." and not "Package required libraries...".
Additionally: You must select a run-configuration in your export settings. So, you could always create an empty main( ) in some class and use it for your run configuration.
Anyway, it isn't guaranteed to work 100% of the time - as you will notice a pop-up message telling you to make sure you check the licenses of the Jar files you're including and something about not copying signature files. However, I have been doing this for years and have never encountered a problem.
Extracting into an Uber-dir works for me as we s should all be using root:\java and have outlets code in packages with versioning. Ie ca.tecreations-1.0.0. Signing is okay because the jars are intact from their downloaded location. 3rd party signatures intact, extract to c:\java. There’s my project dir. run from launcher so java -cp c:\java Launcher
In case you are using Spring Boot, you may want to have a look at this documentation: The Executable Jar Format
Java does not provide any standard way to load nested jar files (that
is, jar files that are themselves contained within a jar). This can be
problematic if you need to distribute a self-contained application
that can be run from the command line without unpacking.
To solve this problem, many developers use “shaded” jars. A shaded jar
packages all classes, from all jars, into a single “uber jar”. The
problem with shaded jars is that it becomes hard to see which
libraries are actually in your application. It can also be problematic
if the same filename is used (but with different content) in multiple
jars. Spring Boot takes a different approach and lets you actually
nest jars directly.
The Spring documentation also lists some alternative single Jar solutions:
Apache Maven Shade Plugin
JDotSoft JarClassLoader
One-JAR
Shadow Plugin (Gradle)
I would advise to use one jar and many libraries in separate jars, not in a single jar. Use separate jar from jar libraries.
Suppose you have such a folder structure:
path/yourApp/yourApp.jar
path/yourApp/lib/lib1.jar
path/yourApp/lib/megalib1.jar
path/yourApp/lib/supermegalib1.jar
All you have to do, add in MANIFEST.MF each of used jar.
Manifest-Version: 1.0
Main-Class: com.company.MyProgram
Class-Path: ./lib/lib1.jar ./lib/megalib1.jar ./lib/supermegalib1.jar
From within the manifest, you grant usage to each library.
Single all in one jar file might be easier to share and distribute, but in fact this doesn't give significant advantages over distributing as an archive and unpack it in some folder where you want to deploy. This will not make your program easier to maintain, faster. It will not make significant hdd usage difference.
I'm using netbeans 7.2 with NBAndroid extension. In my android project, I'm referencing a library (ActionBarSherlock) that is not in jar (can't be, for some reason). The problem is, that netbeans doesn't see classes from that library and gives me errors (package does not exist etc.) However it builds and runs OK, the library is added correctly. Netbeans just doesn't see it.
Here is a screenshot.
Here is similar question, no solution package com.actionbarsherlock.app does not exist
Is there a way to fix this? Thanks for help!
EDIT: So I found a way to solve this, it's more a workaround than a solution. I created a jar file from the library classes called classes.jar. I put it in the libs folder, so netbeans sees it. Than I created custom_rules.xml (it's imported via build.xml). In it I move classes.jar away from the libs folder, so I can build it, and in the end I move it back.
<?xml version="1.0" encoding="UTF-8"?>
<project name="imported">
<copy file="libs/classes.jar" todir="./" />
<delete file="libs/classes.jar" />
<target name="-post-compile">
<copy file="./classes.jar" todir="libs/" />
<delete file="./classes.jar" />
</target>
</project>
The errors you see are because NetBeans doesn't recognize the packages, classes, methods, etc. When you build or run the program, NetBeans resorts to the Android project's Ant script, which apparently is configured correctly to find the classes. As far as compiling and running, you won't have a problem. However, if you want to use NetBeans' autocomplete and error-detection features, you need to configure it to detect your libs. To do this, just right click on your project name in the Project pane and click Properties from the context menu. Next, click on Libraries under Categories on the left. Then click Add JAR/Folder and navigate to the folder with your third-party library. You can select one of the options for the path then click OK. Now NetBeans should be able to find the identifiers and help you write your code.
Rather than doing some tricks in your build script I'd recommend you to use ActionBarSherlock as a library project. You should be able to do this in project customizer (select project, right click, choose properties).
Also see 'Including in YOur Project' bullet 2. in http://actionbarsherlock.com/usage.html
-Radim
I'm using ant to build a project in eclipse. Before the jar is constructed, I want to move some files from another project into a resources folder in this project.
The problem I'm having is that none of the files show up in the jar.
Example:
<project>
<target name="A">
<copy file="../otherloc/file1" tofile="resources/file1" />
<waitfor><available file="resources/file1" /></waitfor>
</target>
<target name="B" depends="A">
<jar destfile="dist/jarfile.jar">
... (actually build the jar)
</jar>
</target>
</project>
So in the above case, "file1" doesn't actually make it into the jar, even though everything else in the resources directory does. Eclipse doesn't show the new file in the resources directory until I refresh, either. It's like, because eclipse doesn't know about it, it's not included in the build.
Any suggestions appreciated. I'm pretty much an Ant noob, so it's possible I'm going about this entirely wrong.
Edit: Alternately, is there some way to just include the file from the other project? I was a little unclear on the best way to get it into the jar. It needs to be in a directory along with some other existing project files.
Did you check both tasks independently?
First to have file1 copied in the proper location.
Then, if B fails to put file1 in the archive, the issue could lie in the tag. Are you sure resource/file1 is handled by a fileset ?
Unfortunately we haven't been able to resolve this, so we just work around it by separating the task into two steps and copying the files before running the build script. Not really a great "answer", but nothing else has been suggested.
I'm working with Android at the moment, trying to avoid using Eclipse (for which I have an irrational hatred).
I need to include an external .jar file (used in my Activity)and have no idea how to link it for ant debug...
I've read up on build.xml files but adding <path id="compiler.classpath">...</...> or <classpath> nodes to the XML doesn't help fix it.
Hope someone can help me out!
Just put it in the libs/ directory. Everything else is taken care of from there -- no Ant script modifications are needed. For example, here is a sample project showing integrating a BeanShell interpreter this way.
I'm deploying my project to a web-server to be deployed with java Web Start. However, Web Start uses modification date to figure out whether to download the resources again (by default).
What I want is a way to only deploy those (jar) files that do not already exist. This is made possible by having build-version numbers on all my jars, so 2 jars with the same name have the same contents.
Notes:
The jar modification dates will always be newer in the build (which is why I'm getting this problem), due to downloading from svn or ivy
There's a way to do this using sun's download servlet, more files etc, but I'm lazy, don't need it, and this (simpler) solution will be more robust in the long term
If you are using ants copy task (you don't explicitly say you are), you could try the present selector: http://ant.apache.org/manual/Types/selectors.html#presentselect.
<copy todir="target">
<fileset dir="src">
<present targetdir="target" present="srconly" />
</fileset>
</copy>
You could attempt to set the granularity attribute very, very high, to basically disable the "copy files with the same name if the source file is newer than the destination" feature.
We had a similar problem, and I wound up implementing my own jar task to do what I needed. The Ant source code is a good place to start; if you're lucky you might be able to just subclass Ant's jar task. It's not nearly as difficult as it might sound, and was much more straightforward than the half dozen workarounds I tried or considered.
We solved this issue with maven and JavaFX and WebStart Plugin that keeps maven layout and version in the JNLP, and using "non-unique" SNAPSHOT will keep the same jar (ignoring the timestamp).
I know it's maven so not good for you, but someone may give it a try?