I think I already know the answer, but would appreciate if someone can confirm it for me.
A) We are including a library in a build using maven-shade-plugin;
B) This library implements singleton(s);
C) Our resulting jar (uber-jar) is, in turn, used in a build of a megaproject;
D) Other jars in a megaproject also using the same library (A);
Would the library (A) be not acting as a singleton across the entire megaproject?
Would we end up with a separate set of static variables for each shaded copy of an artifact?
Would using maven-assembly-plugin instead of maven-shade plugin help?
You described a scenario in which the same class could end up in the classpath more than once.
In such an application the class loader will pick up the first one found when looking for that class. Once it has found it, this class is loaded and initialized by this class loader, and he will not look it up again. Usually that does not lead to problems, and you will still have the singleton as you want: one instance only.
If - for some reason - you have multiple class loaders at hand, they each could load this class from another location. So you could end with several singleton instances.
The shade plugin seems not to be the best tool for that. I would suggest to use it only for standalone applications that you package in a single shaded JAR - the end product.
We always use the assembly plugin because it gives a more fine-grained control over the packaged assembly. But you should not use shaded JARs as dependencies, instead simply use the core libraries. Even if you have the same dependency in various dependency paths in your project, the assembly plugin will package it only once.
Related
I am using different libraries that reference the same library in different versions with different group-ids (relocated). So maven assumes that those are different libraries and puts them both in classpath.
Nevertheless the package-names have not changed, so the same class with the same package-name exist in maven.
This is e.g. the case with org.bouncycastle:bcmail-jdk14-1.38 vs. org.bouncycastle:bcmail-jdk14-138. But since both versions are identical I don't expect trouble.
But with javax.xml.stream:stax-api-1.0-2 vs. stax:stax-api-1.0.1 we have no identical bytecode, so the same class with different content will be in the classpath.
Am I right - and if so, can maven warn me and prevent such problems?
You can use the enforcer rule
https://www.mojohaus.org/extra-enforcer-rules/banDuplicateClasses.html
that allows you to break the build if a class name appears in more than one jar.
No, for Maven those are different artifacts and you can get into trouble. Having multiple versions of the same class on the class path is not a good idea.
I have successfully configured Proguard with Maven to obfuscate a jar, and its dependant jar. I have managed to get both obfuscations to use the same mapping file, so that one jar can call the methods of the other. The problem I am facing, is that Proguard is not keeping unique names across the obfuscated jars; both obfuscated jars contain a class called
f.b.class
As there are two classes called f.b.class (one in each jar), priority is being given to the class inside the calling jar, which is causing problems.
Has anybody experienced this before and are you aware of a solution for this. Currently I am using the
-keeppackagenames
switch to ensure that the package hierarchy remain different so that any duplicated class names do not conflict. Ideally I would like to remove all package names
The switch
-useuniqueclassmembernames
has also been applied but it clearly only applies this to the jar currently being obfuscated. It does don't look and previously obfuscated jars to ensure uniqueness across jars.
Thanks
To resolve this I ended up using the -keeppackagenames option. It is not a solution, but a work around.
I have two projects in my NetBeans window
MyProject
Tester.java
Utilities
Utils.java
The Utils.java file contains a number of static methods written by others that we can re-use. Recently I added a new set of static methods to Utils.java that uses new external jar's. I added the libraries to the Utilities project via Properties --> Libraries --> Add Library
I then proceeded to call these methods from within Tester.java but received java.lang.NoClassDefFoundError exceptions for the classes that were defined in those external libraries. This does not occur when I call the methods from within the Utilities project.
I solved the problem by adding the required libraries to the MyProject project as well, but is there a reason why I have to do this?
You need to have the external libraries in your MyProject as well because it is transitively dependent on those libraries. You are getting java.lang.NoClassDefFoundError because the required classes were available for the Utilities during the compile time to build the jar but those classes are missing at the runtime.
The reason is simple: Utils.class relies on the classes from the library to work. So if you don't have the classes of the library in the classpath, Utils.class can't work. Just like just having an accelerator is not sufficient to make a car move. Without the car engine, the accelerator can't work. The fact that you, as a driver, don't mess with the engine directly, but only through the accelerator, doesn't mean the engine is not necessary. (sorry for this car analogy, but hopefully it makes things clearer).
I have my jar file with its own libs like any other netbeans project. Additionally I have a "plugins" folder to store plugins in a .jar file format. I´m loading the plugins using an URLClassLoader and I´m also executing the proper method without any problem.
However, the jar file in the plugins folder may contain dependencies with other packages (java-mail, for example) who aren´t in my classpath (the plugins folder isn´t in my classpath neither), so I´m worried whether that plugin would be executed correctly. I also don´t know where I should store that dependency.
Could I do something to overcome this issue? Where should I store the plugin dependency?
Thanks.
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
(From the javadoc of ClassLoader)
The paragraph above means that normally, your class loaders form a tree and each one asks its parent before trying to load a class. So in theory, if your dependencies are visible by either the class loader that is loading the plugin, or any of the upstream class loaders, it will work.
However, there are two caveats with this:
If your plugin calls Thread.setContextClassLoader(), that might mess things up. But this should be more of the plugin writers' concern rather than yours.
Web servers typically don't obey this delegation rule to ensure maximum separation between different webapps and the server itself.
(I could probably give a less generic answer if there was some code I could look at.)
According to biziclop, i have put the dependencies inside the plugin folder. It seems to work, but i have needed to download extra packages.
To clarify, i made a plugin using the apache httpClient library. I made the jar file, and then i put the jar file with its dependencies (httpClient and httpCore) inside the plugin folder. When i executed the plugin a "NoClassDefFoundError" appeared. To overcome this issue i have download the "commons-logging" and "servlet" packages and i have added them into the plugin folder.
At least, that´s not my problem because the plugin developer should test the plugin and provide those extra packages, but the fact is that those extra packages are unveiled while testing the plugin and not while compiling it.
Thanks for the help!
an application I have written uses several third party jars. Sometimes only a small portion of the entire 50kB to 1.7mB jar is used - one or two function calls or classes.
What is the best way to reduce the jar sizes. Should I download the sources and build a jar with just the classes I need? What existing tools can help automate this (ex I briefly looked at http://code.google.com/p/jarjar/)?
Thank you
Edit 1:
I would like to lower the size of my third party 'official' jars like swingx-1.6.jar (1.4 MB), set-3.6 (1.7 MB) glazedlists-1.8.jar (820kB) , etc. so that they only contain the bare minimum classes I need
Edit 2:
Minimizing a jar by hand or by using a program like proguard is further complicated if the library uses reflection.
Injection with google guice does not work anymore after obfuscation with proguard
The answer by cletus on another post is very good How to determine which classes are used by a Java program?
Proguard would be an option. It can eliminate unused classes and methods. You can also use it to obfuscate, which can further reduce the size of your final jar. Be aware that class loading by name is liable to break unless care is taken to keep the affected classes unobfuscated.
I've found Proguard quite effective - can be a bit cryptic to understand at the outset. But I don't have any experience with similar to offer a comparison.
First of all, if you use only one class from JAR file this does not mean that this class does not use other classed from that JAR.
The option for you, if you use open source JARs, is to get sources of that JAR, attach them to your project, remove unnecessary stuff and build the changes by yourself.
You could add GenJar as an Ant task and use it to build the JAR. As it says on the library's home page,
GenJar is a specialized Ant task that
builds jar files based on class
dependencies rather than simply the
contents of a directory.
You can find it on SourceForge.