making a single-jar java application - java

If I have a java project that consists of several different types of files (pictures, sound, etc) and multiple jar dependencies, what is a good way to package it all into one single jar that can just be double-clicked?
I know jars by themselves are pretty dumb in that they don't look within themselves for files that they depend on (something I only realized after the slightest bit of frustration (understatement)). -- If jar A depends on classes contained inside jar B, putting jar B inside jar A will not work. Jar A must be in the same directory as jar B.
...Now, I know I could just extract all the files from all the other jars and put everything in the same directory. That would sort of work, but I don't want to do it because: 1. It would be messy and 2. it still wouldn't solve the problem of needing to have the sound files in the same directory as the final jar. (For whatever reason, sound files act the exact same way as internal jars)
Basically, I just want to make it so that the files my application depends on aren't obnoxiously visible and ostentatious. So, if there were some solution where I could put everything inside a jar and have it be the only necessary file to run the entire program, that would be optimal. But, I am willing to accept creative/inventive ways to bypass the problem, such as having a batch script in a parent directory execute the jar or something. (I say "or something" because that exact scenario would only work on windows operating systems. ...you know what I mean!)

Apache Maven plus shade plugin will do exactly what you need.
Check out the "Shade Plugin where a Main-Class is added to the MANIFEST.MF" section here http://maven.apache.org/plugins/maven-shade-plugin/examples.html

You can extract all the JARs and merge them into one common JAR. There are ANT tasks and Maven plugins readily available for doing just this. Additionally, if you're application is properly written, there is nothing preventing you from putting media files and other resources in the JAR as well. You just need to ensure that these resources are "loaded from the classpath", rather than being loaded from the current working directory.

If you use the assembly plugin for maven, you can have it download dependencies, build modules and produce an executable jar.

If you use Maven, then I suggest the shade or assembly plugins.
If not, then read this: Easiest way to merge a release into one JAR file

If you are building an executable jar and have other jars that you need to be available in the classpath, there is a Class-Path: line in the jar's MANIFEST.MF file that lists entries (including jars and directories) to include in the classpath when the main class is run.
I usually use my IDE or ant to build such executable jars and set the Class-Path: header.
By the way, to make your jar executable, set the Main-Class: line in your MANIFEST.MF file.
Here is an example from an executable jar I build:
Main-Class: com.example.app.MyAppCLI
Class-Path: log4j.jar driver.jar libraries.jar
And here is a corresponding ant target to build this:
<target name="exejar">
<jar destfile="myapp.jar"
basedir="bin"
include="**/app/*.class">
<manifest>
<attribute name="Main-Class" value="com.example.app.MyAppCLI"/>
<attribute name="Class-Path" value="log4j.jar driver.jar libraries.jar"/>
</manifest>
</jar>
</target>
If you will use a tool like ant to build your executable jars, it will make the process more easy to repeat, and will also handle strange edge cases for you, like what happens when a header line in MANIFEST.MF gets too long.

If you use Maven, you may a appreciate the onejar-maven-plugin. One major benefit is that all your dependency jars stay in jars, and your code is in its own jar. All of those jars are put in a bigger jar, which is made executable, thus avoiding some potential classpath issues. Read the usage guide and this blog post for more information.

have a java project that consists of several different types of files (pictures, sound, etc) and multiple jar dependencies, what is a good way to package it all into one single jar that can just be double-clicked
A better way to deploy a rich client app. is using Java Web Start. The end user would never see a Jar, and could be provided a desktop short-cut or menu item to launch the app.

Related

Android: compile APK with ANT, common libraries from outside the project folder

I'm running a web service that allows users to create simple android apps. I'm using ANT to automate the building of the APKs:
Create the project folder (eclipse-style)
Copy libs (ad sdks) to the libs folder
Run ant
The problem is that even if the libs are always the same for all apps, they are pre-dexed for every single app and the build process takes much longer than needed. I understand this is the normal behavior for the setup I have now.
What I'd like to do is keep all the libs in a common folder on the server and tell ant to use those, preventing the pre-dexing process to happen for each app.
I'm not sure what settings to use in ant.properties or build.xml to make this happen.
Thanks!
You can use the lib switch to specify a library
-lib specifies a path to search for jars and classes. Or put the libs in the standard directories.
Any jars or classes in the directories of the path will be added to Ant's classloader. The order in which jars are added to the classpath is as follows:
*.-lib jars in the order specified by the -lib elements on the command line
*.jars from ${user.home}/.ant/lib (unless -nouserlib is set)
*.jars from ANT_HOME/lib
First of all, a tiny improvement; put your shared libs where you want and than, for each Android project that requires one or more of these libs, just create a symlink in the project libs/ directory which point to the desidered lib. You can save space and time, specially if the webserver is used by many users.
To avoid the multiple-dexing stuff, you may try to build a standalone .dex file of your libs and use it dinamically inside your apps using a ClassLoader.
You can start from this: http://android-developers.blogspot.in/2011/07/custom-class-loading-in-dalvik.html

Is it correct or incorrect for a Java JAR to contain its own dependencies?

I guess this is a two-part question. I am trying to write my own Ant task (MyFirstTask) that can be used in other project's build.xml buildfiles. To do this, I need to compile and package my Ant task inside its own JAR. Because this Ant task that I have written is fairly complicated, it has about 20 dependencies (other JAR files), such as using XStream for OX-mapping, Guice for DI, etc.
I am currently writing the package task in the build.xml file inside the MyFirstTask project (the buildfile that will package myfirsttask.jar, which is the reusable Ant task).
I am suddenly realizing that I don't fully understand the intention of a Java JAR. Is it that a JAR should not contain dependencies, and leave it to the runtime configuration (the app container, the runtime environment, etc.) to supply it with the dependencies it needs? I would assume if this is the case, an executable JAR is an exception to the rule, yes?
Or, is it the intention for Java JARs to also include their dependencies?
Either way, I don't want to be forcing my users to be copying-n-pasting 25+ JARs into their Ant libs; that's just cruel. I like the way WAR files are set up, where the classpath for dependencies is defined under the classes/ directory.
I guess, ultimately, I'd like my JAR structure to look like:
myfirsttask.jar/
com/ --> the root package of my compiled binaries
config/ --> config files, XML, XSD, etc.
classes/ --> all dependencies, guice-3.0.jar, xstream-1.4.3.jar, etc.
META-INF/
MANIFEST.MF
I assume that in order to accomplish this (and get the runtime classpath to also look into the classes/ directory), I'll need to modify the MANIFEST.MF somehow (I know there's a manifest attribute called ClassPath, I believe?). I'm just having a tough time putting everything together, and have a looming/lingering question about the very intent of JARs to begin with.
Can someone please confirm whether Oracle intends for JARs to contain their dependencies or not? And, either way, what I would have to do in the manifest (or anywhere else) to make sure that, at runtime, the classpath can find the dependencies stored under the classes/ directory? Thanks in advance!
The term 'JAR file' can mean at least two things, or rather, has at least two facets to its meaning. Most basically, it means a container format: basically, a ZIP file with a META-INF directory. More refinedly, it means this container used as a way to package class files.
In the sense of being a container, there is no intent with respect to contents; the file could contain class files, other JARs (in either sense!), etc. But in the sense of being a packaging of code, i believe the intent for JAR files proper is for them not to contain any dependencies.
If you have a read of the JAR File Specification, you'll find there are several allusions to the storage of class files, but nothing about storing other JAR files. Correspondingly, if you look at the implementation of the JAR file classloader in the JRE, it can't do anything useful with nested JARs.
Furthermore, the JAR specification does detail a mechanism for dealing with non-nested dependencies: the Class-Path attribute. This lets a JAR file make relative references to other JAR files in the filesystem.
Now, in-the-sense-of-a-packaging JAR files are not the only use of in-the-sense-of-a-container JAR files. WAR, EAR, and RAR files (and more besides) are all JAR files used for particular purposes. Each of those is capable of containing other JARs: WARs can contain in-the-sense-of-a-packaging JAR files, and EARs can contain those and also WARs. However, those are quite different beasts than in-the-sense-of-a-packaging JAR files. It's worth noting that special classloaders, that are not in the Java standard library, are needed to make use of them.
The way that WARs etc can collect many JAR files together is indeed very useful, and it's a real shame there's no generic mechanism for doing this in Java outside of Java EE. It would be great to have an 'application archive' or 'meta-archive' format that simply bundled some JARs.
So, you're left with this problem of users needing 25 JARs in order to use your plugin. You have roughly two options.
First, you accept the pain, and distribute your plugin as a zip full of JARs, which users will have to unpack.
Secondly, you join the 21st century, and use a build tool and distribution mechanism which handles dependencies automatically: in practice, that means using Gradle, or Maven, or some other tool (such as Ant) in concert with Ivy, to obtain dependencies from Maven Central, and then then releasing your code along with a POM file which lists those dependencies. Users can then download your JAR and your POM, and have their own build tool obtain the dependencies.
If you do go the second route, it might be prudent to also release a zip of the dependencies, for the benefit of users who are not using automatic dependency management.
The intent (AFAIU) is for JAR files to behave like native code shared object files (.so on Unix, .dll on Windows). Generally, an application will install several shared object files as siblings, plus an executable with which to launch them.
An executable JAR is more like a standalone executable, and so it is more common to include all dependencies (similar to the way a statically-linked native code executable contains all its dependent objects directly).
Unfortunately, the default ClassLoader is not able to load classes from nested JARs. It is possible to write a ClassLoader that does. Or you can use one someone else has written. From the description of your problem, it sounds like Jar Jar Links is exactly what you're looking for.
Is it correct or incorrect for a Java JAR to contain its own dependencies?
There are use cases where it is correct for a JAR file to contain its own
dependencies. If you would like to support users who do not use modern
dependency management, you may want to provide a JAR file containing your Ant
task code as well as all dependencies. The more powerful, flexible, and modular
approach is to publish versioned JAR files to the Maven repository that
only contain your project code.
1) JAR file containing your project code and all dependencies
Pros
Easy to download and the only setup for end users is including a
<taskdef> in their Ant build files
No setup required to publish Maven artificats
Example Ant target to build JAR
<target name="jar" depends="compile"
description="Creates a standalone JAR of all class files and dependencies.">
<jar destfile="${my.ant.task.jar.file}" update="true">
<fileset dir="${build.classes.dir}" />
<zipfileset src="${lib.dir}/javax.inject.jar" />
<zipfileset src="${lib.dir}/guice-3.0.jar" />
<zipfileset src="${lib.dir}/guice-multibindings-3.0.jar" />
<zipfileset src="${lib.dir}/guice-assistedinject-3.0.jar" />
</jar>
</target>
Cons
If end users of your Ant task already have some or all of the dependencies
included in their projects, then they will end up with redundant copies of the
dependencies
The JAR file could be very large
2) JAR file containing only your project code published to Maven Repository
Pros
Users may fetch any version of your Ant task that you have published to
the Maven repository, which provides more flexibility in releasing new
versions of your task while allowing existing users to continue using previous
versions to avoid possible regressions
Avoids duplicate copies of common dependencies (except where different versions of a dependency cause errors)
JAR file will be small
Cons
Need to learn about the following:
Maven Repository
Publishing Maven Artifacts - see
Ant + Ivy specific preparations and procedures
Apache Ivy - Dependency manager integrated with Apache Ant
For reference, the Java™ Tutorials provide a good summary of JAR files.
Lesson: Packaging Programs in JAR Files
The Java™ Archive (JAR) file format enables you to bundle multiple files
into a single archive file. Typically a JAR file contains the class files and
auxiliary resources associated with... applications.
The JAR file format provides many benefits:
Security: You can digitally sign the contents of a JAR file...
Decreased download time: If your applet is bundled in a JAR...
Compression: The JAR format allows you to compress your files for efficient
storage.
Packaging for extensions: The extensions framework provides a means by
which you can add functionality to the Java core platform, and the JAR file
format defines the packaging for extensions...
Package Sealing: Packages stored in JAR files can be optionally sealed so
that the package can enforce version consistency. Sealing a package within a
JAR file means that all classes defined in that package must be found in the
same JAR file.
Package Versioning: A JAR file can hold data about the files it contains,
such as vendor and version information.
Portability: The mechanism for handling JAR files is a standard part of the
Java platform's core API.
"Jar Jar Links" is only good for a standalone applications. But not for Ant.
If your project has the same dependencies and they are upgraded to newer versions later on, like xstream-*.jar, then there will be a conflict, and the wrong version may be picked up. In the worst case there will be MethodNotFoundException. That's why it is a bad practice to include dependencies in a single jar.
What's a problem with "I don't want to be forcing my users to be copying-n-pasting 25+ JARs"?
That's the easiest solution. And the best, because you will avoid problems in the future.
Now, when you see the inconveniences of Ant, you might want to compare it to Gradle. With Gradle you get tasks a bit similar to Ant and you don't need to provide any dependency jars. All dependencies for you will resolve Gradle. And like in Ant you still can create your tasks.
Some java application vendors use the following scenario to distribute their application which depend on other jars, it reminds of static linking. At the stage of building the jar all dependencies (being also jars) are unpacked. When building the final jar, they include both their freshly compiled classes and the classes extracted from dependencies.
Possible issues:
Applications cannot reuse the libraries as they are contained in the app. Usual static linking issue.
The licenses of the repacked libraries must be respected. Usually it will be ok to repack them, but sometimes additional care must be paid to their license files, which may happen to be inside their jars.
AFAIK it's not possible to have jars inside the jar or it will be impossible to specify a classpath for them. Hence the repacking procedure.

Why doesn't an executable jar acknowledge jars inside it?

I made an executable jar that depends on other jars with the command prompt using the format
jar cvfm MyJarName.jar manifest.txt *.class dependentJar1.jar dependentJar2.jar
The jar was made properly and everything seemed fine... But when run, it gets runtime errors because it can't find the class files that my project refers to. Exploring the created jar, the other 2 jars that it depends on are in there just as they should be and they are listed in the manifest.mf class path, but for some reason java is dumb and doesn't actually look inside those jars.
When those 2 jars are in the same directory as the owner jar, java is able to find them and it works fine. But I don't want this; I want those jars to actually be INSIDE it. What's the deal? How can I make an executable jar with other jars inside it work?
You can use something like OneJar or jarjar (or any of several others) to pack up everything inside of a single jar.
If you're building with Maven you could use the Maven Shade Plugin.
You could use a custom classloader like JarClassLoader that will allow precisely what you want.
I don't think you could. Maybe it's somehow possible with customized classloader, but not from-the-box. Use maven shade plugin.
If you use an Ant Builder in Eclipse and put the referenced jars in a library accessible to your project, Eclipse will extract the necessary classes and include them in your distributable jar file.

How to merge a.jar,b.jar,c.jar,d.jar into one single jar called e.jar in eclipse IDE project? can you tell the steps to do this?

In my project I'm using many jar fiels to support the project..as the number of jar files increases I want to move all jar files into a single jar and make use of that.can you provide me useful links that can help me to do this.
Plenty of options -
In Eclipse you can
Create a normal java project with a sample main class
Add jar files to the project
Export it as a runnable jar, which should repackage the all the jars into one.
Do it through an ant task -
<jar destfile="combined.jar">
<zipgroupfileset dir="lib" includes="*.jar" />
</jar>
More tools like jarjar, onejar (and many more) etc are available to do the repackaging.
If you are willing to use ant (which can be called from Eclipse) there are some solutions in this other question here: Clean way to combine multiple jars? Preferably using Ant
I also like proguard, which combines jars and also strips out unused code.
I would recommend you use a maven pom file to maintain your dependancy's. It has a pretty steep learning curve at first, but once you get the hang of it it makes your life so much easier. You would just add all your dependencies in the pom and maven would download and maintain them itself.

How do I include external JARs in my own Project JAR

I have a Java application and created a JAR file and deployed it.
The App uses external JARs such as the Log4J JAR. When creating my JAR file, how do I include all external dependent JARs into my archive?
In order to get my App working, I'm having to copy the Log4J JAR into the same directory as my own JAR which kinda defeats the purpose of the jar. Wouldn't it be more elegant to have 1 single JAR file to deploy?
If you use Eclipse, You can extract all included files into one runnable jar like this:
Right click on your project name from Package Explorer and select Export.
In Export screen, select Java -> Runnable JAR file and Next.
Fill in the Runnable JAR File Spec screen and Finish.
You can choose whether to package dependency jars as individual jar files or extract them into the generated JAR.
You could use something like One-JAR to package your Java application together with its dependency into a single executable Jar file (One-JAR uses a custom classloader to make JARs nesting possible).
You have to expand the library jars into the same place where your compiled classes go, then make a jar from that. Depending on how your build process is set up, there may be multiple ways to achieve this. It's not rocket science - a jar is just a zip archive with a META-INF directory at the root level.
Keeping JAR separate is better as it is easy to upgrade only the specific JARs to its new versions without touching any other configuration. As of your issue of having to copy each file to same location as of your JAR, you can always use Java CLASSPATH and include any JAR to your application's class path.
A JAR is not itself capable of nesting other JARs, as you discovered.
Traditionally, one would distribute a ZIP archive or other installer that would unwind the application JAR (yours) as well as any support JARs in the appropriate location for classpath access. Frequently, then, the application was invoked through a script that invoked the primary JAR and created a classpath that listed the support JARs.
As other posters have noted, you have some options to create a super-JAR if that's what you want.
You can use Maven + assembly plugin (http://maven.apache.org/plugins/maven-assembly-plugin/)
BTW, probably that's not the easiest way, if you did not work with maven.

Categories

Resources