MANIFEST.MF file for JAR Used in Web App? - java

I've developed a utility library that will be used in many of our enterprise Java applications. This library has numerous additional dependencies that also need to be on the classpath. I'd like to avoid forcing our developers to add a zillion entries to their MANIFEST.MF files, and let them instead just include my library. Is there any way that my library's MANIFEST.MF file can reference its dependencies and have them picked up by the enterprise applications that will be using my library?
I've tried referencing them in my library's MANIFEST.MF file using the full path to the dependencies on the filesystem. That didn't work. I end up with ClassNotFoundException errors for all of my dependencies. Is there something else I should be trying?

When you create a web application, you'd normally put it in a WAR file. The idea is that you bundle the required dependencies in that WAR file, by adding the jars to the /WEB-INF/lib folder inside the WAR. Web containers (like in a Java EE application server) know of this structure and will include those jars on the classpath.
If your library has additional dependencies, just tell the users about it and either redistribute them with your library if the license allows it, or tell them where to obtain them. When using a decent tool for creating a web app like an IDE, Ant with Ivy, or Maven (or a combination of these), then handling and bundling dependencies should be no problem.

Alternatively, this works so long as you stick to the format very carefully, i.e. stick to exactly two spaces before each "file:" etc:
Manifest-Version: 1.0
Main-Class: package.TestClass
Class-Path: file:/D:/WebServer/Tomcat/shared/lib/BlueCove.jar
file:/D:/WebServer/Tomcat/shared/lib/classes12.jar
file:/D:/WebServer/Tomcat/shared/lib/comm.jar
file:/D:/WebServer/Tomcat/shared/lib/FTP.jar
file:/D:/WebServer/Tomcat/shared/lib/FTP2.jar
file:/D:/WebServer/Tomcat/shared/lib/iText.jar
file:/D:/WebServer/Tomcat/shared/lib/j2ee.jar
file:/D:/WebServer/Tomcat/shared/lib/jmxremote.jar
file:/D:/WebServer/Tomcat/shared/lib/jmxri.jar
file:/D:/WebServer/Tomcat/shared/lib/jmxtools.jar
file:/D:/WebServer/Tomcat/shared/lib/jpos15.jar
file:/D:/WebServer/Tomcat/shared/lib/js.jar
file:/D:/WebServer/Tomcat/shared/lib/mail.jar
...
file:/C:/WebServer/Tomcat/shared/lib/soap.jar
file:/C:/WebServer/Tomcat/shared/lib/sqljdbc.jar
file:/C:/WebServer/Tomcat/shared/lib/tools.jar

I've done this with a number of tools. It is a truly horrible hack but seems to work reliably.
Give them a special manifest to use. Something like:
Manifest-Version: 1.0
Main-Class: com.xxx.yyy.zzz.YourSpecialClassThatHacksTheClassPath
Real-Main-Class: com.ppp.qqq.TheirMainClass
In your special class, screw around with the classpath (not easy), read the manifest "Real-Main-Class" entry (a bit easier) and launch their main from that (not really difficult at all).
Obviously this will not work with a .war file.

Even I had the same problem. As mentioned above, the solution was to have exact two space after file:/ and one space after .jar file and at the end, press enter key.
I know this is not a neat solution, but it works. enjoy.

Related

Declare dependency to war file in Gradle

Based on the gradle docs, to define external jars means adding to build.gradle the following snippet (considering you have {project_root}/libs/foo.jar) in place:
dependencies {
runtime files('libs/foo.jar')
}
However, using the same dependency declaration for *.war files doesn't work. Is this even possible? The project I'm trying to depend on builds to a war file.
Since war layout is different from jar file standard layout, it's not possible to declare war a dependency file to a java project. Possible ideas:
Clone the project and define it as a dependency (very stupid idea, I'm ashamed that I suggest sth like that)
Contact the author and ask him/her if you can just copy the class you need to use. If you can copy the class along with the credits.
Contact the author and ask him/her if it does make sense to make the codec open source (I know it is right now) and release it as a standalone jar library (maybe along with other classes used in the project).

how do i force a method call from specific versioned jar when multiple versioned jars are present

i have two versions of same library , say abc-1.0.jar and abc-2.0.jar
i cant avoid either for my application to work , parts of the code are not yet migrated to 2.0
how do i force a class object and methods to be from abc-2.0.jar ? but not from 1.0.jar in my module.
any help greatly appreciated !
Regards
Sashank
There is no totally general, and yet simple solution.
The simplest solution is put the 2 JAR files on the classpath, with the 2.0 JAR file ahead of the 1.0 JAR file. This will probably work for your use case, but it won't work if the there are cases where you need to use the older version of some class.
Another solution is to put the class in question into a new JAR file and put it on the classpath ahead of both the 1.0 and 2.0 JAR files. But reorganizing the JAR files may not be an option.
The complicated way is to write a custom classloader for the 1.0 and 2.0 JAR files. This is a lot of work, and probably unnecessary.
If, whenever there is an ambiguity (same class in both jars) you want to use the 2.0 jar, then you can just ensure that the 2.0 jar is before the 1.0 jar on your classpath.
If it's just one (or a few) classes where you want to use the 2.0 jar, and others where you want to use the 1.0 jar, the problem becomes considerably more complicated.

Problems including jar files outside the jar file containing the manifest

Basically let me first explain what I am aiming to do. I have a dynamic ETL transformer app written in JAVA. Due to the dynamic nature of this app I have to be able to add plugins jars to the app in a location outside of the apps jar file.
Basically would like to have the following directory structure:
AppFolder
|- plugins/
|- configs/
|- mainApp.jar
If possible I would like to be able to use wildcards in my manifest to dynamically add jars located in the plugins folder.
Unfortunately all I have tried so far has failed. I have tried to use both relative paths and absolute paths neither have worked (with or without wildcard).
If I however include the plugins folder in the main app's jar file itself it works fine given that I don't use wildcards.
So my question is, is it actually possible to have dependencies outside of a jar or do they always have to be contained within.
The other question is regarding the usage of wildcards. i have looked at [the java documentation] (http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html) to no prevail unfortunately.
some examples of what I have tried so far:
../plugins/*
../plugins/plugin.jar
/abolute/path/to/plugins/*
/abolute/path/to/plugins/plugin.jar
and unfortunately none of them have done the trick so any help would be very much appreciated...
Yes you can have dependencies outside the jar. But wildcards are not supported for specify dependant jars.
The jars need to be explicitly specified in your manifest, and the location needs to be relative to where the application is run from
A better option for you may be to use the Extension Mechanism
java -Djava.ext.dirs=/abolute/path/to/plugins/ ......
If you have control of the code you could always add a JarClassLoader and load the jars dynamically.
http://java.sun.com/docs/books/tutorial/deployment/jar/jarclassloader.html

Java distribuion as jar file containg config, libs and deps

I am developing a framework that needs a lot of stuff to get working. I have several folders inside of my Eclipse project that are needed
[root]
- config
- src
- lib
- serialized
Also there are important files like the log4j.properties and the META-INF dir inside the src directory.
I wonder if there is a way to distribute one JAR containing all essential files so my gui will just have to import one jar. I guess that I have to exclude the config folder in order to make the framework configurable.
I also wonder, if there is a way to move for example the log4j.properties to the config dir so that I have one config folder containg all needed configurations?
Thanks for help and advise on this matter!
Marco
Yes, but not really. You can take all your dependencies, unpack them and simply merge them into a bigger jar. This is what the maven jar plugin does if you make a jar with dependencies. The only problem is that this might result in conflicting files (suppose two of your dependencies contain a log4j.properties). This is one of the problems when doing the above with some of the spring libraries for instance.
I think someone actually wrote a classloader that allows you to bundle the whole jar inside of your jar and use it as is. I'm not sure how mature that is though and can't at the moment recall the name.
I think you're better off distributing all your dependencies separately. Setting up the classpath is a bit of a pain but surely java programmers are used to it by now. You can add dependencies to the Class-Path header in your manifest file, in simple cases. Bigger libraries have to rely on the classpath being set up for them though.
As to the second part of your question, probably dropping the conf/ directory under META-INF is enough for its contents to be picked up. I'm not sure about this. I'm fairly sure it will always be picked up if you put its contents at the top level of the jar. In any case, this is a distribution problem. You can easily have a conf/ directory inside your source tree and have your build scripts (whatever you might be using) copy the files in it to wherever is most convenient.
As to your users configuring. Try to establish some conventions so they have to configure as little as possible. For things that must be configured, it's best to have a basic default configuration and then allow the user to override and add options through his/her own configuration file.
In terms of the resources, it is possible except that if you do that you are not going to be able to load resources (non class files) from the filesystem (via a file path).
It's likely that you're currently loading these resources from the file system. Once in the jar you need to load them as class path resources via the class.getResourceAsStream or similar.
As for the dependent jars you may have, it's common practice for these to be placed as extra jars on the classpath. I know it's complicates things but developers are used to doing this. The nature of the java landscape is that this is inevitable. What the spring framework for example does is supply a bundled zip file with the core jar and the jar dependencies included.
Is your library going to be used in an EE context or an SE context? If it is an EE context then you really don't have to worry about configuration and class path issues as the container takes care of that. In an SE context it is a lot more tricky as that work has to be done manually.

Classpath including JAR within a JAR

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.

Categories