How to load a resource from compound JAR? - java

I have the following problem:
I am writing an application that uses some of the JARs from the Netbeans Platform. To be exact, I am only using the Netbeans Visual Library for creating some graphs. This can be done without using the Netbeans Platform by extracting 3 JARs from the platform. This is all working well, except for 1 problem.
Some Background
I am using the Java Simple Plugin Framework (JSPF) to handle my plugin management. So I have an application that basically consists of a skeleton framework, and then depending on which plugin JARs it finds, it can do various things, one of which is drawing graphs. The JAR plugin for this functionality has all it's dependant libraries inside. This is done by exporting the JAR as an artifact in IntelliJ, which will unJAR all the dependant libraries and reJAR them inside yours (so everything you need is there).
The Problem
What seems to be happening though, is that when it tries to start use the classes from the embedded libraries, it works fine, but when it needs resources (.png specifically in my case), it complains that it cannot find it.
My Thoughts
The only thing I can think of why it is not working, is that it could be since the plugin JAR is not in a classpath. Could this be it?
Is there anyway to specify a classpath directory in the MANIFEST maybe? Otherwise must I create my own ClassLoader and manually load all the JARs in the plugins directory?
Thank you!
UPDATE:
I have subsequently pinpointed that it is indeed a problem with the classpath. If I place my compound library on the classpath, everything works perfectly. The problem I experience now though is:
If I copy the library to /Library/Java/Home/lib/ext/ it works fine. If I execute the application with java -cp "/path/to/plugins/myLib.jar" -jar Application.jar it does not work.
How can I load all the jars in the plugins directory into my application so the resources inside them can be used?
Thanks again!

So I have finally figured out what was happening. When creating a executable jar, the MANIFEST.MF file overrides any classpath you specify in the command-line, which basically renders it useless if you want to specify external jars. This seems to be a general problem that has been logged since Java 1.3 already.
My simple solution is to simply not create a executable jar, and then launch the application with a script:
java -cp App.jar:plugins/* my.package.structure.App
which works perfectly.

The default classloader's do not load classes in nested jars. You'll need to write your own classloader to get the classes in the nested jars.
You can check out this jspf article...
"I forgot: Adding dependencies as JARs inside JARs is not possible, because it would not work in all scenarios (e.g., applets); IIRC also tools like Eclipse would have problems if you used classes with unresolved (read: runtime-resolved-dependencies). To my knowledge there is no established way yet to gracefully handle nested JARs in all circumstances."
http://code.google.com/p/jspf/wiki/UsageGuide

Related

Include library for JAXB into JAR file without using Maven

I have a Java project in Eclipse that uses javax.xml.bind.JAXB classes.
Starting the application from inside Eclipse works perfectly.
However, when I export the project as (runnable) jar file and run it using java -jar myfile.jar it terminates with a java.lang.NoClassDefFoundError: javax/xml/bin/JAXBException.
Also playing around with the three options for Library handling in Eclipse Runnable JAR File Specification (extract, package, sub-folder) does not solve the problem - in fact, no libraries are exported in any case.
It seems that the library for JAXB (it seems to be rt.jar) is not considered as required library to be included into the jar file. However, when running the jar file, it is not found nevertheless.
I have read that the library must be added to the classpath but this seems strange to me as rt.jar is part of the standard libraries. Is there something special about this library?
Currently, I do not use Maven or something similar for dependency and build management and if possible I want to avoid it for the future. I think, there also must be a way without Maven.
I found several posts here on SO and in Google but was not able to work out a solution for me.
Thank you very much!
As remarked in the comments, Eclipse probably uses a different Java version than your system (by default). The JAXB API and implementation is not available in JRE 11.
To work on all versions of Java, your best option is:
Download the JAXB RI distribution. Nowadays I'll choose version 3.0 (which is binary incompatible with the one in Java 8, since it uses jakarta.xml instead of javax.xml for the packages name) as in Juliano's answer:
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-ri/3.0.0/jaxb-ri-3.0.0.zip
Copy the 4 files jakarta.activation.jar, jakarta.xml.bind-api.jar, jaxb-core.jar and jaxb-impl.jar from the mod folder into the library folder of your project (let's say lib),
Add the 4 libraries to the project's "Build Path",
Make sure you use JAXB 3.0 throughout your code (the packages of the annotations and classes start with jakarta.xml)
Run the application once in Eclipse, so it updates the Run Configuration (or update the classpath of the Run Configuration yourself),
Export the project to a JAR file.
Among the three export options proposed by Eclipse: "extract required libraries" will create a so-called fat jar (everything in one JAR-file). It works, but it deletes the licence notices in the JAXB jars (so it can not be distributed). "copy required libraries" is your best option, but then you have to move the jar file together with the subfolder. _". "package required libraries" will not work, since jars in a jar are not read by the JVM (unlike JARs in a WAR package).
Edit by the author of the question:
The above worked for me well except that I experienced small differences how the two libraries (javax.xml in Java 8 and jakarta.xml in version 3.0) handle #XmlAttribute annotations. In javax.xml, I could place an annotation without further arguments on the public getter-method, e.g.
#XmlAttribute
public String getDescription() {
return "";
}
And this worked when the attribute name in the xml file is description. However, with jakarta.xml I had to add the name of the attribute:
#XmlAttribute(name="description")
public String getDescription() {
return "";
}
Just in the case, that others experience the same problem.
I thought about this myself too, since I am new to java.
There is a description of a Extension Mechanism in the java tutorials (SE), but it is no longer used since deprecated by Oracle. See, just to know of what I am talking about: https://docs.oracle.com/javase/tutorial/ext/index.html
What was this Extension thing in a nutshell: just drop your jar files inside the jdk lib and you could use the import keyword in all your classes to use the new jar file.
However, others had to do the same thing in their computers to run a class which imported your own update to the jdk.
Maven do something like the above. It searches on the pom file which other jar files it should include in your jar when you build an application. Hence, it may run anywhere.
Another way of looking into this is the answer which you should try to do.
A clunckier way of doing what Maven does without its pom structure is to create a new folder inside your src folder and copy the jakarta.xml.bind-api.jar. Just like when you create an object (aJavaBean) and need to use it in another class.
The file you need to include in your library is available at:
https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-ri/3.0.0/jaxb-ri-3.0.0.zip
Finally, extract the classes inside this newly created folder and use the import keyword in the classes that depend on it just like when you create your own classes.
Another thing you should try is to use the manifest file when making your jar.
https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
This tutorial shows how to include a classpath to the files you need to run as a dependency. Make sure that everything you need is inside the newly created jar file.
Also, set the entry point in the manifest, so your application can run just using
java -jar MyJar.jar
in the command line.
The easiest way is to use JDK 8 (or older JDK) that has embedded the required jaxb library. The hard way requires that you set your CLASSPATH variable pointing to each required jaxb jar file.
From spec at https://javaee.github.io/jaxb-v2/doc/user-guide/release-documentation.html#a-2-3-0, the following jars are required using a java version 11 or above.
jaxb-api.jar
jaxb-core.jar
jaxb-impl.jar
A good article on this question is https://www.jesperdj.com/2018/09/30/jaxb-on-java-9-10-11-and-beyond/

Class-Path setting in executable jar doesn't seem to work for plugins

I've looked here and the wider web to find a solution to this. There's related material, but I've been unable to find anything useful about my specific question.
I'm working on some Java software that needs to accept plugins. I don't want to use a fancy framework like OSGi, and ServiceLoader seems to offer the right level of support. I've basically got it working but am having a problem with classpaths. My directory structure is as follows:
progfolder
|___________ plugintest.jar
|___________/plugins
|________ plugin1.jar
|________ plugin2.jar
If I run plugintest.jar with java -jar plugintest.jar then it doesn't find the plugins even if I add ./plugins/ (or variations of this) to the Class-Path: in the manifest. Reading suggests that this only works for classes, not jars, so I've tried putting the classes from the two plugins inside plugins both directly and within their full path of directories, but with no success.
I'm not allowed to add -cp plugins/* to add the plugins folder to the classpath if I'm using the -jar option. To get round this, I can run using java -cp plugintest.jar;plugins/* com.plugin.test.Main and this works as expected - the two plugins are detected and accessible via code, but the command line is a bit clunky, although I could live with it, if it's the best option.
I found another solution where I create a classloader for jars found in plugins, which works in this simple case, but reading suggests I might run into security issues in a more complex application.
Is there a way to fix things so I can simply run with java -jar plugintest.jar without having to do my own class loading or is this just the way it is?
Ok, so at least a partial answer, following more experimentation. Putting the class files in the plugins directory does work, after all, but you have to remember to include the META-INF directory and META-INF/services. The file in the services directory has to include references to all the plugins.
It would be nice if there was a solution that allowed the plugin jar files to be used directly, but creating a class loader seems to be the only way to do this (that I've found, at least), and this may cause security issues, as previously noted.
last time I faced with similar problem [1]. I found answer in java documentation [2]:
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.
[1] https://github.com/narvi-blog/01-exec-jar#dependency-jar-files-within-an-executable-jar-are-not-so-easy
[2] https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html

What method/class etc. uses this .jar?

I have a big application as an enterprise scale, there are lots of developers working on it; it is a Java Spring app, we use Eclipse as development environment. We have lots of libs, which means jar files in the lib directory. I can't touch to these jar files so easily, because whenever I change some jars, application may get failed/unstable because of little changes of the jars' new versions. On the other hand, as this application is a big and old project, I can't know what jar is really needed and which line, what function or class uses/needs this jar exactly.
So I have a problem now: As an example naming a-lib-1.0.jar is old jar and a-lib-2.0.jar is new version; I need to use a-lib-2.0.jar but a-lib-1.0.jar is already in lib directory. How can I know that a-lib-1.0.jar is needed or not without testing by deleting it?
And how can I find the line/method which uses this jar?
Please check Tattletale from JBoss.
What you need is a Dependency Walker.
Try this:
http://www.dependency-analyzer.org/
http://depfind.sourceforge.net/

Deploy Java Web Applet with Third Party Dependencies

I have made a Java Applet that depends on some third party libriaries as well as a DLL that is called via JNI. These will obviously not be installed on clients computers. Is there anyway to package these third party libriaries and external DLLs within my Applet's JAR so that it can be run by any client with a base Java install?
Using JNI within a web application is a recipe for heartburn, particularly in Windows.
You obviously can't deploy the DLL within your WAR file and will probably require a separate installer be run by the deployer beforehand.
You'll have to ensure that your java.library.path is setup correctly, which is usually done by settings in the application server rather than something deployable in the WAR file.
The way that JNI links the native class with the library will cause class loader headaches if you try to run more than one instance of that web application. One way to work around this is to move the containing JAR file into a shared class loader (like Tomcat's common folder) but that has its own complications.
Is there anyway to package these third
party libriaries
In Eclipse, under the Java project properties (right click), I can do a:
Export... => Export as Runnable JAR
Then I tell it to pack my jar dependencies into that exported JAR.
The exported JAR will have all its dependencies packed into it.
alt text http://www.ubuntu-pics.de/bild/97131/selection_016_mg6IDm.png
I am no expert on the topic, but there are working solutions to all these problems. They might give you a headache, but some things can only be done this ways (and I am quite sure that your problem with the Entrust CSP is one of those).
You can take a look at OpenSign from the OpenOCES-project. The magic happens in org.openoces.opensign.client.applet.bootstrap.
I also believe that JNLPAppletLauncher solves the same problem, but I have no experience with i.

Whats best way to package a Java Application with lots of dependencies?

I'm writing a java app using eclipse which references a few external jars and requires some config files to be user accessable.
What is the best way to package it up for deployment?
My understanding is that you cant put Jars inside another jar file, is this correct?
Can I keep my config files out of the jars and still reference them in the code? Or should the path to the config file be a command line argument?
Are there any third party plugins for eclipse to help make this easier? I'm using an ant build file at the moment but I'm not sure I know what I'm doing.
Is there an equivelent of the deployment projects in Visual studio, that will figure out everything you need and just make an installer? I've used install4j before, and it was powerful if no where near as automated as .Net deployment projects.
Cheers.
There is no one 'best way'. It depends on whether you are deploying a swing application, webstart, applet, library or web application. Each is different.
On point 2, you are correct. Jar files cannot contain other jar files. (Well, technically they can, its just that the inner jar file won't be on your classpath, effectively meaning that jar files do not contain jar files).
On point 3, you certainly can reference config files outside the jar file. You can typically reference a config file as a file or a resource. If you use the resource approach, it typically comes from the classpath (can be in a jar). If you use a file, then you specify the filename (not in a jar).
In general, most Java developers would use Apache Ant to achieve deployment. Its well documented, so take a look.
(1) An alternative to ant that you may wish to consider is maven.
A brief intro to maven can be found here.
For building a JAR, maven has the jar plugin, which can automate the process of ensuring all dependent jars are listed in your jar's manifest.
If you're using eclipse, then download the maven integration as well.
(2) Another alternative is to use OneJar (disclaimer: haven't tried this myself).
The answers vary depending on what kind of thing you're building.
If you're building a library, it's best to distribute your work as a jar file. It's possible to refer to your jar dependencies via the Class-path attribute in your jar manifest, although I generally think that's uncool. That attribute was designed for applets and it's used infrequently enough in libs that when this technique pulls stuff into the classpath (particularly common stuff the user might already be using), you can get unexpected version conflicts. And it's hard to track down why you're seeing them.
Publishing a jar to a Maven repo with pom info to track dependencies is an excellent choice for libraries as well. If you do that, please publish your Maven coordinates in your docs!
If you're building an app, the two popular choices are to distribute a zip/tar/whatever of a deployment structure OR to use an installer program. If the program is a server-ish kind of thing, the former is far more common. The latter is more common for clients. Generally, the installer program is just going to lay out the deployment structure and maybe do some extra tasks like installing in OS-specific locations.
To build your deployment structure (aka "kit") you'll want to create a repeatable process in whatever build system you're using. Ant has copious examples of this and Maven has the assembly plugins that can help. Generally you'll want to include a jar of your code, any dependencies, scripts to start the program, maybe a JRE, and any other resources you might need.
If you want to create an installer, there are many options both free and commercial. Some folks I know have recently had good experiences with the free IzPack but check out your options.
You should try FatJar. It's an Eclipse plugin that with just a right click at the Project can build a JAR file with all you need to run the application, including the necesary third party JAR.
We use it everyday, in conjuction with JSmooth to create the executables, to deploy our software packages to our customers, and works like a charm.
Well, if you are speaking of deployment of a standalone desktop application:
Before we switched to web start we have been creating three deployment archives, one for windows, one for mac and one for other platforms.
On windows we have successfully used the Nullsoft Scriptable Install System (known for it's usage by the older winamp versions) and its ant task, although some drawbacks are:
It is only usable on windows AFAIR
You have to do some work by hand, i.e. customizing the wizard-created script AFAIR
It can create a windows installation with start menu entries on the other hand. There also exists an eclipse plugin for integrated NSIS shell script editing.
On Mac OS X there is an ant task to create an .app file from your java files so that you can start it like a native os x application. But beware of not writing any setting to your home dir and using the the application dir instead.
For others you have to expect they are in a un*x env and deploy your app with a shell script to start the application.
In any case you may have to deploy your custom policy file to get access rights for your application.
If you want to get rid of all the packaging and stuff you should seriously consider using web start. We have saved much time since switching to it, i.e. simplified our deployment process, takes care of updates etc.
Update 2014
Use maven assembly plugin, see section "Creating an executable jar"
Ant. It's not the best thing in the world, but it's standard, it's apache, and it works.
There's some good examples on the web how to make a simple build.xml for any, and it's got some features like the 'war' task that knows how to put all the basic stuff (classes, web.xml etc) in the jar file for you.
You can also tell it to pick up other config files and jars and it will happily do it.
It's also really smart about what to compile. You give it a directory, and it finds all the java files and builds them only if their classfile is out of date, so you get some of the traditional make functionality for free without much effort.
You could look at other java projects (e.g. JMeter, SquirrelSQL, JEdit, Cernunnos, etc.). Each package their applications slightly differently, so consider your goals when you review these.

Categories

Resources