Reference jars inside a jar - java

I have a jar whose content looks as shown below,
Below is my manifest file
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.7.0_06-b24 (Oracle Corporation)
Main-Class: org.sai.com.DerbyDemo
Class-Path: derby.jar derbyclient.jar derbynet.jar derbytools.jar
When i try to run the jar, it has thrown a ClassNotFoundExcception meaning it isn't referencing the jars inside the outer jar.
In the Class-Path attribute, how can I reference jars (derby.jar, etc) inside the actual jar?

You will need a custom class loader for this, have a look at One Jar.
One-JAR lets you package a Java application together with its dependency Jars into a single executable Jar file.
It has an ant task which can simplify the building of it as well.
REFERENCE (from background)
Most developers reasonably assume that putting a dependency Jar file into their own Jar file, and adding a Class-Path attribute to the META-INF/MANIFEST will do the trick:
jarname.jar
| /META-INF
| | MANIFEST.MF
| | Main-Class: com.mydomain.mypackage.Main
| | Class-Path: commons-logging.jar
| /com/mydomain/mypackage
| | Main.class
| commons-logging.jar
Unfortunately this is does not work. The Java Launcher$AppClassLoader does not know how to load classes from a Jar inside a Jar with this kind of Class-Path. Trying to use jar:file:jarname.jar!/commons-logging.jar also leads down a dead-end. This approach will only work if you install (i.e. scatter) the supporting Jar files into the directory where the jarname.jar file is installed.

You can't. From the official tutorial:
By using the Class-Path header in the manifest, you can avoid having
to specify a long -classpath flag when invoking Java to run the your
application.
Note: The Class-Path header points to classes or JAR files on the
local network, not JAR files within the JAR file or classes accessible
over internet protocols. To load classes in JAR files within a JAR
file into the class path, you must write custom code to load those
classes. For example, if MyJar.jar contains another JAR file called
MyUtils.jar, you cannot use the Class-Path header in MyJar.jar's
manifest to load classes in MyUtils.jar into the class path.

In Eclipse you have option to export executable jar.
You have an option to package all project related jars into generated jar and in this way eclipse add custom class loader which will refer to you integrated jars within new jar.

Default implementations of the classloader cannot load from a jar-within-a-jar: in order to do so, the entire 'sub-jar' would have to be loaded into memory, which defeats the random-access benefits of the jar format (reference pending - I'll make an edit once I find the documentation supporting this).
I recommend using a program such as JarSplice to bundle everything for you into one clean executable jar.
Edit: Couldn't find the source reference, but here's an un-resolved RFE off the Sun website describing this exact 'problem': http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4648386
Also, you could 'test' that your program works by placing the library jar files in a \lib sub-directory of your classes directory, then running from the command line. In other words, with the following directory structure:
classes/org/sai/com/DerbyDemo.class
classes/org/sai/com/OtherClassFiles.class
classes/lib/derby.jar
classes/lib/derbyclient.jar
From the command line, navigate to the above-mentioned 'classes' directory, and type:
java -cp .:lib/* org.sai.com.DerbyDemo

if you do not want to create a custom class loader. You can read the jar file stream. And transfer it to a File object. Then you can get the url of the File. Send it to the URLClassLoader, you can load the jar file as you want.
sample:
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("example"+ ".jar");
final File tempFile = File.createTempFile("temp", ".jar");
tempFile.deleteOnExit(); // you can delete the temp file or not
try (FileOutputStream out = new FileOutputStream(tempFile)) {
IOUtils.copy(resourceAsStream, out);
}
IOUtils.closeQuietly(resourceAsStream);
URL url = tempFile.toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
urlClassLoader.loadClass()
...

Add the jar files to your library(if using netbeans) and modify your manifest's file classpath as follows:
Class-Path: lib/derby.jar lib/derbyclient.jar lib/derbynet.jar lib/derbytools.jar
a similar answer exists here

in eclipse, right click project, select RunAs -> Run Configuration and save your run configuration, this will be used when you next export as Runnable JARs

Related

Execute JAR from lib directory

I have the "SomeJarA.jar" with the followiung structure:
SomeJarA
|
|--lib/SomeJarB.jar
|--com/.../SomeClass.class
Im trying to execute the "SomeJarB.jar" in "SomeClass.java" using:
Runtime.getRuntime().exec("java -jar SomeJarB.jar");
Is this possible?
As far as I know, if there's a way to achieve this, it is going to be a tricky and highly unsupported arrangement. I'll look around for some supporting documentation, but I'm inclined to say "No."
This related link might help, though.
EDIT: Found the reference I wanted. It's here. Specifically, there's a note which says, "The Class-Path header points to classes or JAR files on the local network, not JAR files within the JAR file or classes accessible over internet protocols."
This is from user #Tezar and Question Classpath including JAR within a JAR
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/
See the linked Question. It seems like the sanest method for jars in jars and avoids third-party class loaders. If you feel like upvoting, go to Tezar's answer and upvote that entry, not this one, as this is just a copy.

Running jar doesn't work

We are trying to make a jar out of our class files but somehow it does not work. The creation of the jar-file works fine with
jar -cvfm client.jar Mainfest.txt /home/pi/Desktop/Client/*.class
But when we try to run it comes with a classnodefferror. Our application is using 2 property files - one for database and one for log4j.
The directory and the one subdirectory included looks like this:
See link - image nr. 1 and 2
When we try to execute the jar file it shows this error:
(See link image 3)
Normally when we run it, we type (see link image 4)
And the manifest-file looks like this (see link image 5)
We have tried different solutions like changing paths, leaving out environment (-D) etc. and we really can't figure out what we do wrong.
There are several problems here:
It is better to explicitly mention each included JAR file in the manifest - wildcards like * isnt working too well
You can't use absolute paths in a JAR file - use relative paths instead
So, 1) change Manifest.txt to
Main-Class: Client
Class-Path: . includes/log4j-1.2.17.jar includes/mysql-connector-java-5.1.33-bin.jar includes/sqlitejdbc-v056.jar includes/RXTXcomm-2.2pre2.jar includes/..... (repeat for all pi4j JAR files)
And, 2) copy ALL the JAR files you need (including RXTXcomm-2.2pre2.jar and the pi4j JARs) into the include/ subdirectory, and change command to
jar -cvfm client.jar Manifest.txt *.class *.properties includes/*

Jar executable with sqlite driver

I made a code that connects to my sqlite driver which is in the CLASSPATH and reads some database file. I want to create an executable which can be used on computers that don't have the sqlite driver.
If I do:
jar cvfe exec.jar main_class
I will get "class not found: org.sqlite.JDBC" when running with
java -jar exec.jar
What should I do to make the executable work?
Edit:
I don't know if it makes any difference, but this is the JDBC driver I use:
https://bitbucket.org/xerial/sqlite-jdbc
You need to include the library inside the JAR. Maybe you don't know this, but JAR files are just ZIP files, so you can change their contents easily. Here are some quick instructions on how to do it. Assuming your JAR file is named exec.jar, and the JAR of the library you want to include (the JAR you downloaded) is driver.jar
Change your file name from exec.jar to exec.zip.
Extract all the contents of exec.zip into folder exec/
Change your library file name from driver.jar to driver.zip
Extract all the contents of driver.zip into folder driver/
Copy the contents of driver/ into exec/, but do not copy the META-INF folder. If a pop-up asks if it's ok to merge the folders, click yes.
Compress all files in exec/ into exec.zip
Rename exec.zip to exec.jar (replace the original).
You can include any java library inside a JAR using this method.
Here is the doc:
C:\Windows\System32>jar /?
Illegal option: /
Usage: jar {ctxui}[vfmn0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
And so I think the command you need is:
jar cvfe exec.jar main_class main_class

wsimport -clientjar generates classes in (default package)

I'm using the -clientjar wsimport parameter to export my WebService into a jar.
>wsimport -d C:\webservice -keep -clientjar webservice.jar http://localhost:8080/WebService?wsdl
A folder with the source code (.java files) and a webservice.jar are created.
The jar looks like this:
com
|
company
|
webservice
|
a bunch of .class files
META-INF
|
wsdl
|
wsdl file
However, when I put it on the WEB-INF/lib folder in my project, the classes are in the (default package) and are named like
com\company\webservice\file.class
I can't understand why. I've also used the -p parameter to specify a package name but it doesn't work.
Any clues?
There are two options of achieving this , both works like a charm.
And both options can be automated from ant\gradle you name it .
1.To use -clientjar and then to repack the sources
2.Manually insert the wsdl into jar and customize the wsdLlocation URL
Assuming you have C:\WSDL\SO\stas.wsdl
(I was running on windows)
CD C:\WSDL\SO\
First option
C:\WSDL\SO>wsimport -clientjar StasWebServiceClient.jar stas.wsdl
This creates StasWebServiceClient.jar jar file , but when importing it to eclipse, the sources are not importable , because of the topic problem (default package).
=> Unzip the jar file to current folder , you can use 7zip, or any other great zip tool , or you can run
C:\WSDL\SO>jar xf StasWebServiceClient.jar
to unzip the jar .
Folder hierarchy should look like
C:\WSDL\SO\META-INF
C:\WSDL\SO\stas.wsdl(original wsdl)
C:\WSDL\SO\StasWebServiceClient.jar(generated jar file)
C:\WSDL\SO\META-INF\wsdl(created by -clientjar)
C:\WSDL\SO\META-INF\wsdl\stas.wsdl(copied by -clientjar)
C:\WSDL\SO\com\...
/* all generated classes\sources */
C:\WSDL\SO\com\...
=> Do
C:\WSDL\SO>jar -cvf StasWebServiceClientCorrect.jar com META-INF
this will create another jar , StasWebServiceClientCorrect.jar , which now has the correct packaging .
Second option
=> Run wsimport
C:\WSDL\SO>wsimport -keep stas.wsdl
to generate the code .I always like to have -keep option there , but it's up to you.
=> create META-INF folder
C:\WSDL\SO>mkdir META-INF
=> Create META-INF/wsdl folder
C:\WSDL\SO>cd META-INF
C:\WSDL\SO\META-INF>mkdir wsdl
=> go one folder up .
C:\WSDL\SO\META-INF>cd ..
=> Copy stas.wsdl file into META-INF\wsdl\stas.wsdl
C:\WSDL\SO>copy stas.wsdl META-INF\wsdl\stas.wsdl
=> Create a jar archive
C:\WSDL\SO>jar -cvf StasWebServiceClient.jar com META-INF
Import the jar to workspace.
When you will be creating the actual call to the service , use :
StasService stasService = new StasService(StasService.class.getClassLoader().getResource("META-INF/wsdl/stas.wsdl") )
I think the problem here is that -clientjar option is meant for an entirely different purpose than the OP expects.
The purpose of the -clientjar option is to embed a copy of the WSDL inside the generated artifacts so that it can become part of the application's jar file. The benefit of having a bundled WSDL (and actually using it of course) is that the web service consumer does not have to make a call to the endpoint to download the WSDL every time it initiates itself.
Links:
https://weblogs.java.net/blog/ramapulavarthi/archive/2010/09/03/wsimport-clientjar-option-ease-client-side-web-service-progra
Using jaxws-maven-plugin with -clientjar option
I had the same problem. Finally decided not to use the -clientjar option and generated a jar manually with jar.exe and works.
1.) Unzip/unjar clientjar
2.) jar file using
jar cvf <jarName>.jar <root_folders>
example:
jar cvf weather.jar com META_INF
I used -clientjar so it will do all the work for me, but used my favorite file archiver to unzjar and jar it again.

Class not found despite of classpath in MANIFEST

I have this jar:
/mybundle.jar/
de/mybundle/myclass.class
lib/mysql.jar
META-INF/MANIFEST.MF
With the following MANIFEST.MF
Manifest-Version: 1.0
Class-Path: lib/mysql.jar
Main-Class: de.mybundle.myclass
It all seems perfectly correct for me, but when I run
java -jar mybundle.jar
I get a NoClassDefFoundException when the class tries to instantiate one of the MySQL-Library classes.
What did I do wrong?
You can't bundle jar files in other jar files. The paths specified in the Manifest are relative to the location of the jar file you're calling, so in your case relative to the location of mybundle.jar.
You have two options:
Either put the MySQL jar in the lib directory outside of your mybundle.jar.
Create a fat jar, which contains all classes from the required jar files in addition to your own classes. This is available from within Eclipse or Maven.
If your mybundle.jar is in c:/foo, then your mysql.jar has be in c:/foo/lib. The Class-Path in the manifest is relative to the executable JAR the way you've written it.

Categories

Resources