Assuming there are two jars of different library versions on a classpath, e.g.
java -cp A-2.1.jar:A-2.2.jar ...
The package and class names in the first and second jars are the same, but class implementation is different. Is it specified whether root jvm classloader will try to find a class in A-2.1 before A-2.2?
The problem is that AWS EMR adds hadoop jars to a classpath and some of its dependencies are of older versions. However, our application needs to use new versions of the same libraries, so will prepending the classpath with newer versions of libraries be enough or is shading a recommended practice in this case? http://docs.aws.amazon.com/ElasticMapReduce/latest/DeveloperGuide/emr-hadoop-config_hadoop-user-env.sh.html
From the Setting the Class Path documentation:
The order in which you specify multiple class path entries is
important. The Java interpreter will look for classes in the
directories in the order they appear in the class path variable.
That said, overriding the dependency JARs of another library will always be risky since the library provider might not have tested that combination, so you'll either need to ask them for reassurance, do your own testing, or shade/repackage the classes as you suggested.
Related
I have a Scala Akka Application which connects to HBase (currently CDP earlier HDP) deployed on rancher; Never faced any trouble when connecting to HDP hbase; Since recent HDP to CDP change, with the same image we are getting no method found on one of the dependency's class in one of the container, where as another container of same image connects to hbase properly.; even though the jar exists in the same image and classpath also.
one of the noticeable difference is change in the order of classpath.
Does change in the classpath order will effect the jars availability.
Does java libraries/classes would load in different order when they would hit a faster CPU cycle at startup.
What could be the reason for such "no class method found".
It certainly can, if the same class file is present in different classpath entries. For example, if your classpath is: java -cp a.jar:b.jar com.foo.App, and:
a.jar:
pkg/SomeClass.class
b.jar:
pkg/SomeClass.class
Then this can happen - usually because one of the jars on your classpath is an older version than the other, or the same but more complicated: one of the jars of your classpath contains a whole heap of different libraries all squished together and one of those components is an older version.
There are some basic hygiene rules to observe:
Don't squish jars together. If you have 500 deps, put 500 entries on your classpath. We have tools to manage this stuff, use them. Don't make striped jars, uber jars, etc.
Use dependency trackers to check if there are version difference in your dependency chain. If your app depends on, say, 'hibernate' and 'jersey', and they both depend on google's guava libraries, but hibernate imports v26 and jersey imports v29, that's problematic. Be aware of it and ensure that you explicitly decide which version ends up making it. Presumably, you'd want to explicitly pick v29 and perhaps check that hibernate also runs on v29*. If it doesn't, you have bigger problems. They are fixable (with modular classloaders), but not easily.
*) Neither hibernate nor jersey actually depend on guava, I'm just using them as hypothetical examples.
For example, if you use maven, check out the enforcer plugin. (groupId: org.apache.maven.plugins, artifactId: maven-enforcer-plugin).
My bet is that there is another version of the jar somewhere in CDP, and occasionally it is loaded before the version that you ship with your project, causing the error.
So, when your container starts, try logging from which location the conflicting class is loaded. This question might help you: Determine which JAR file a class is from
I have a Apache Commons library (commons-io.jar) added to my project classpath and I want to remove this library classes from my classpath during runtime and then I want to add after some work.
Is there any way to remove all classes from classpath during runtime and add these classes after some operation?
public static void main(String [] args){
//Some apache commons operations
//Remove apache commons classes from classpath
doSomeWork();
//Add apache commons classes to classpath
}
The problem you are experiencing is known as classpath hell.
There is no such functionality in Java. The reason behind this is the design of classloaders, which can only load classes and not unload them. In all likelyhood, this is not going to happen even in the future.
You could use a special classloader to load different classes from different jars, but even this way you quickly stumble into other limitations. For example, a classloader should only load the class if the parent has not yet loaded it. This means that other parts of your code cannot use apache-commons.jar. You could separate your program into two parts and have each run in its own classloader/classpath, but then you'll realize you can't share objects between them anymore. And so on.
In the end, you can either (a) try to find a combination of jars that works with both, (b) fix one part of the program so that it works with the other jar, or (c) break the program into two parts and have each run in separate JVMs.
You could use the maven-enforcer-plugin in your pom to force specific versions of the transitive dependencies.
Conflicting library version in a java maven project
MATLAB is configured to search its static java class path before searching the user-modifiable dynamic path. Unfortunately, the static path contains quite a number of very old public libraries, so if you are trying to use a new version you may end up loading the wrong implementation and get errors.
For instance, the static path contains an old copy of the google-collections.jar, which has long been supplanted by Google's guava library and which has some of the same class names (e.g. com.google.common.base.Objects). As a result, if you invoke a Guava method that uses a newer method of one of such a class, you will end up getting surprising NoSuchMethodErrors because the google-collections jar is found first.
As of R2012b, MATLAB lets you specify additional jars to add to the static path by putting a javaclasspath.txt file in your preferences folder, but that adds jars to the end of the path, and doesn't let you override jars that are built into MATLAB.
So what is the best way around this?
I got an official response from Mathworks:
As of MATLAB R2013a (also in R2012b), classes can be added to the front of the static Java class path by including the following line in javaclasspath.txt:
<before>
Any directory that is after this line in javaclasspath.txt will be added to the front of the static Java class path. This is an undocumented use of javaclasspath.txt as of R2013a.
But overall in MATLAB, the ability to add classes to the front of the static Java classpath is not available through javaclasspath.txt in MATLAB 8.0 (R2012b).
MATLAB searches for classpath.txt in the following order:
In the startup directory. As of MATLAB 8.0 (R2012b) a warning will be shown if the file is found there and it will be ignored.
In the first directory on the MATLABPATH environment variable. (This environment variable is used in the bin/matlab shell script on Linux and in general is not used by the end-user).
In the toolbox/local directory.
Although the MATLABPATH environment variable of point 2 is normally not used by end-users we can use it in a workaround to allow reading a custom classpath.txt outside of the toolbox/local directory.
On Windows:
You will need to create the MATLABPATH environment variable. The first directory on it should be your directory with the custom classpath.txt AND you will also need to add the toolbox\local directory as second option. So from a cmd prompt you could do:
set MATLABPATH=c:\Users\user\Documents\myMATLABClasspath;c:\Program Files\MATLAB\R2012b
\toolbox\local
matlab.exe
One hack that appears to work is to add the jar to the top of the classpath.txt file that can be found in your MATLAB installations toolbox/local folder. Unfortunately, this is automatically generated and may get rewritten at some unspecified time, such as when you install new toolboxes, so this approach would require you to have some way to notice when this happens and reapply the hack.
If you're distributing a jar that's intended to be used with matlab, it may be better to use proguard as described at http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava.
If you specify that all of your classes and their (public) fields and methods are to be preserved and include guava as a program jar (not a library), then it will rename all of guava's methods and update your compiled bytecode to reference the new names.
It seems a bit hackish, but depending on the audience, it may be significantly easier than teaching your users about static vs. dynamic classpath, and it won't break any matlab code that depends on the old behavior.
Instead of obfuscating the package as suggested by #user2443532, I have found it easier to "shade" the conflicting package instead of obfuscating it - unless you actually need obfuscation. One easy way to do this is to build your package using Maven and use the maven-shade-plugin. Internal calls are modified automatically, so you don't need to modify any of the Java code.
Direct calls from Matlab will need to be modified - for example, calls to com.opensource.Class become shaded.com.opensource.Class.
For more info on shading, see What is the maven-shade-plugin used for, and why would you want to relocate Java packages?
I am using Eclipse and I have got two libraries included in my project, foo.jar and bar.jar. In both JARs there is a class FooBar.java that includes the method getFoobar(Object xy).
Now I would like to load the method getFoobar(Object xy) either from foo.jar or from bar.jar on the basis of a properties file:
config.properties:
choice=foo
If choice==foo then the method of Foo.jar shall be picked, elsewise the method getFoobar(Object xy) from Bar.java. To make things more complicated the method getFoobar(Object xy) has in its method declaration objects loaded from another JAR which is included in foo.jar (and bar.jar respectively).
So my question is. How can I get the methods of the JARs respectively by Reflections? I have not yet found a solution. Is there a way to solve this issue?
Thank you.
Emrah
You can only load one or the other methods without getting complicated with class loaders. A simple workaround is to rename the package of one or both libraries with jarjar This allows you to have everything in foo.jar start with foo. and everything is bar.jar start with bar. and this avoids any confusion. I have seen this used to be able to load several version of Xerces based on configuration (and use them concurrently in the same app)
Write your ClassLoader that reads the property. Alternatively, you can use -D option in the jvm arguments
-Dchoice=foo
and in the code that loads the class from foo.jar or from bar.jar you can switch on this property value:
String choice = System.getProperty("choice");
if (Main.CHOICE_FOO.equalsIgnoreCase(choice)) {...}
...
OSGI can give you a similar capability.
How can I get the methods of the JARs respectively by Reflections?
You can't.
Reflection does not allow you to select between the two versions of the class. In fact, as far as the JVM is concerned, there is only one version; i.e. the one that appears earliest on the classpath.
You can solve this problem by defining multiple classloaders, each with a different classpath. However, there are complications:
If you manage to load both versions of the class into a running application, you will find that instances of the respective classes have different types, and you won't be able to convince the JVM otherwise.
When your application loads another class that statically depends on one of these classes, it will bind to the version of the class that is on the classpath of the dependent classes classloader. And you can't change that. So, uses of the class name in declarations / typecasts / etc in the dependent class will refer to the version of the class found by the dependent classes classloader, not the other one.
The upshot is that you can't use these same-named classes like regular classes ... especially if both versions need to be loaded in the same JVM. It is a better idea to give the two versions of the class different names. You don't gain anything by making the class names the same.
I am building java jar file using ant. I need to include additional jars using "zipfileset src="xxx.jar" "zipfileset src="yyy.jar" and both xxx.jar and yyy.jar have the classes with the SAME fully-qualified class names. So the resulting jar file has duplicate class names. What are the possible implications of having duplicates?
Thank you.
If they're duplicate implementations, nothing–it wouldn't matter which is loaded.
If not, you're at the mercy of class load order, and may get a different version than you want.
It is specified that classpath entries will be searched in the order listed (as per this classpath doc). but that's only relevant if you're in complete control of classpath creation (unlike in a web app, for example).
(With the caveat that classpath wildcarding makes the order non-deterministic.)
In general this situatioin is highly non recommended
and should be avoided.
Jars in java are just containers for your class files. java uses classloaders that look at the classpath and load class files from there. so if you have 2 jars A.jar and B.jar that have the same class x.y.Foo inside, the class from the jar that comes first in the classpath will be loaded.
So, if your classpath is A.jar,B.jar (in this order) the class Foo from A.jar will be used in runtime.
This inconsistency can lead to very hard-to-fix bugs from my experience
whats mean duplicated? it's obvious that you can not have 2 classes with the same name in the same package (even your project will not compile), but if you mean that you have 2 classes with the same name in different packages that's "is ok".
I agree with Dave.
Can you separate them by namespace to avoid the pitfalls he suggests?
Another answer which I don't see discussed here is that if you plan on signing your jars, aars, apks then it signjar will complain that you have duplicate entries
jarsigner: unable to sign jar: java.util.zip.ZipException: duplicate entry: com/foo/bar/baz.java