Same class is in multiple jar - java

I have several jars containing the exact same class.
Lets say for example, A.jar and B.jar have the same class Hello.class. What will happen if I run the following:
java -classpath A.jar;B.jar com.testing.testcode
My question is whether it will run or not? As from my understanding class loader will scan the classpath and it will return whatever Hello.class it finds first.
I know I can avoid this problem if I use OSGi for loading a particular class.
But what my question is – whether this will run or not? Or the java -classpath A.jar;B.jar com.testing.testcode will break down for having two version of the same class.

This will work and the first Hello class in the classpath will be used, so in this case, the one from the A.jar

The class path is a "path". The first match is taken.
There is a boot class path which is examined first and this included the JARs in the JRE. BTW You can prepend these and override system classes but you are not supposed to and it might not be allowed in your license agreement.

Whenever you include the jar in your classpath, on startup/ on application class gets loaded into the container.
Now whether your code will work or not will depend on which jar gets loaded first in your container and accordingly if you are lucky then in your current setup it will work else you might get some error saying that method1 doesn't accept Type1 it should accept Type2.

Related

How to exclude a specific jar from java classpath

I am using this below structure to set classpath in my java application.
How can I exclude a specific jar that is loading in runtime.
#!/bin/sh
export JAVA_HOME=/usr/local/java
PATH=/usr/local/java/bin:${PATH}
cd /home/ala/DevDaily/Musubi
THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
Perhaps this similar question can help you?
Deny access to certain class files in a dependency jar
If your case is more complex, and you have another dependency which needs the 'old' implementation of the method and you cannot modify it another solution might be to update the class in the older jar with a 'smarter' class which can hold two implementations dependent on your two callers.
The following code will obtain and print the name of the caller's package and class, after which you can decide your different behaviors.
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
String name = stackTrace[1].getClassName();
System.out.println(name);
This solution is incredibly ill-advised, and defeats the entire purpose of the jar, but it might be a quick fix to your problem.

Java: Two jars in project with same class.

I have a java project that is using two imported jars with the same class (com.sun.mail.imap.IMAPFolder). Is there a way to explicitly say which jar to use when importing the class? Using:
import com.sun.mail.imap.IMAPFolder;
would seem to use the class in order of build path order but this does not seem to be the case for some reason causing
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.lang.NoSuchMethodError: com.sun.mail.imap.IMAPFolder.idle()V
at com.woodbury.GCM.HelperGmailMonitor.doEmail(HelperGmailMonitor.java:104)
at com.woodbury.GCM.Launch.listen(Launch.java:16)
at com.woodbury.GCM.Launch.main(Launch.java:10)
... 5 more
at runtime. I am building the project in eclipse.
When a class is loaded, the first implementation that matches the requested fully qualified name that is visible to the relevant ClassLoader is what gets returned. Any other implementations with the same fully qualified name are effectively hidden to that ClassLoader.
What this means in a standard Java SE application is that the first code base (e.g. a jar) listed on the classpath with the required class, provides it, and all other code bases' implementations of the same fully qualified class are hidden.
Example:
Assume that A.jar contains the compiled class
package com.stackoverflow.example;
public class Hello {
public static String getGreeting(){
return "Hello, A!"
}
}
Assume that B.jar contains the compiled class
package com.stackoverflow.example
public class Hello {
public static String getGreeting(){
return "Hello, B!"
}
}
Note that in both of the above classes have the same fully qualified name.
Assume main class is
import com.stackoverflow.example.Hello;
public class ExampleMain {
public static void main(String[] args){
System.out.println(Hello.getGreeting());
}
}
If I were to invoke my program with
java -cp A.jar:B.jar ExampleMain
the output is: Hello, A!
If I reverse the classpath like so
java -cp B.jar:A.jar ExampleMain
the output is: Hello, B!
You cannot do what you ask just in your Java source. Java was not designed for that.
This is a bad situation which can only be handled reliably with custom class loaders, each providing one of the jars you need. Since you are asking this question in the first place this is probably not the way you should go yet since that opens up a LOT of new time consuming problems.
I would strongly suggest you find out why you have two different versions of the same jar in your classpath and rework your program so you only need one version.
Yes, there is a way to fix the issue. In my scenario, I have two classes with same name and same path and eclipse always imports the wrong one. What I have done is changing the jar order in the build path and eclipse will pick the first one in the build path.
If you are using an IDE, you can set the order of exporting the files to the class loader.
I work on eclipse and I use maven. When I install the project using maven, it produced many extra jars (which i hadnt defined in my dependencies) and there was a file org.w3c.dom.Element which was present in 2 jar files and 3rd instance of the same file was also in JRE7.
In order to make sure the correct file is picked up, all I had to do was to go to Java Build Path -> Order and Export. Select the Jar file I wanted the classloader to give more preference and move it up with the button "Up".
This is how it looks.
Please note that this image is for eclipse. But for other IDEs there would definitely be a similar way to work this out.
1) In general: Yes you can have the same class in different .jar files: you just disambiguate them with a fully qualified package name. The "Date" class (present in java.util and java.sql) is a good example.
2) If you have two DIFFERENT .jar files that have the SAME fully qualified package names ... chances are, you've got a conflict. Even if you can hack around the InvocationTargetException by playing with the class loader, you might still encounter other problems. In this case, it sounds like maybe your two .jar files have two different implementations of the JavaMail API. I don't know.
3) The safest bet is to satisfy all your program's references WITHOUT risking a conflict. I believe if you took took the "official" .jar's from Oracle's JavaMail web page, you can do this:
https://java.net/projects/javamail/pages/Home
'Hope that helps!

Overriding classes in Java

Can I use ClassLoader's definePackage to override some packages from inside a jar?
For example, the application currently contains "javax.xml.bind" from abc.jar. If I call ClassLoader.definePackage(def.jar), in which the def.jar contains another version of javax.xml.bind, can I replace the classpath for the entire application to point to that of def.jar? Thanks.
No, you definitely can not use ClassLoader.definePackage to "override" some packages from inside a jar.
If I understand correctly, you want to make your JVM load any class under javax.xml.bind from def.jar while all other ones from abc.jar. In this case you can (in my personal order of preference):
1) Put def.jar before abc.jar in the CLASSPATH. This requires that no class you want loaded from abc.jar is present in def.jar.
2) Unzip def.jar, abc.jar, or both, and remove any conflicting classes so it is really irrelevant which jar comes first in the CLASSPATH. Then re-zip them. Or you can do this only on one jar and put it before the other.
3) Use a configurable classloader (sorry, no public domain one that I know of; let me know if you find one). This could be an interesting topic for an OS project, except that several initiatives with similar (but much broader) objectives are already ongoing, some at the core of the language.
4) Create a classloader for this purpose, probably extending the default one.

Load methods and classes from build bath in Java

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.

Two classes with same name in classpath

If I have two classes with same name say Matcher.java in two different jar in my classpath which one will be picked up by JVM , is there anyway I can suggest JVM to pick a specific one ?
Quoting Oracle:
Specification order
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. In
the example above, the Java interpreter will first look for a needed
class in the directory C:\java\MyClasses. Only if it doesn't find a
class with the proper name in that directory will the interpreter look
in the C:\java\OtherClasses directory.
The example mentioned:
C:> java -classpath C:\java\MyClasses;C:\java\OtherClasses ...
So yes, it will load the one appears in the classpath that specified first.
The first one found in the classpath. ie, the first jar containing your class will be used.
You can't control it from within the JVM, but you can control the classpath - make sure the one you want is listed/found first in the classpath.
there is a way for you to specify where the class should be picked from.. you can create your own class loader which would load classes according to your requirement.
you can use your class loaded in 2 ways
Pass it as a parameter to jvm (java -Djava.system.class.loader
=com.somepackage.YourCustomClassLoader com.somepackage.YourMainClass)
Use the class loader programatically to load a specific class (refer the links provided).
here are some useful links on class loading
Oracle - How to write your own class loader
A very nice post on writing custom class loader
Use the fully qualified path of the class when using it. But if you mean the class with the same name also has the same package - fix the class-path.

Categories