Is it right, that in case of duplicate classes,
the one in "classes" will be taken?
Consider in a web application the class A will be available directly in
WEB-INF/classes
and a part of a jar in
WEB-INF/lib
Will the one in classes always win?
This depends on the class path - the classpath entry that comes first will initially win. Also, if there are multiple class loaders, a class loader that has loaded the the dependent class will be tried first. It should not depend on if classes are in the jar or not. Documented here.
In general, do not do this as having multiple classes under same name is a true recipe for disaster.
Related
How do I determine the name of the jar that is dynamically loading my jar? Is it possible? I have attempted many variants using ClassLoader but with no success.
Thanks in advance.
Let me explain why I need the name of the "loader jar". In our container we have the following lines:
URLClassLoader classLoader = new URLClassLoader(new URL[] { artifact.getFile().toURI().toURL() });
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, artifact.getArtifact().getFile().toURI().toURL());
Class<?> processorClass = classLoader.loadClass(className);
Object processorClassInstance = processorClass.newInstance();
When the loaded class is instantiated, newInstance above, it's properties files is external to the jar the class resides. The configuration files are in a directory named after the container jar that contains the class that executed the lines of code above. So, if the deployed container jar is called RedcapTDP.jar the configuration files are in "C:...\RedcapTDP". When the RedcapTDP.jar is deployed it dynamically loads the configured maven artifact which in turn will read it's configuration file from the RedcapTDP directory.
I hope that makes it clear!
How do I determine the name of the jar that is dynamically loading my jar? Is it possible?
Taking you literally, no, it is clearly impossible. In fact, it doesn't even make sense. Java does not "load" Jars at all, though it may load one or more classes contained within a jar. When it does so, it is the VM loading the class, not any jar loading it.
Interpreting you a bit more liberally, perhaps you are asking "how do I determine the class whose dependency on one of the classes in my jar is causing my class to be loaded, and how do I determine from which jar file that other class was loaded?" Unless your control extends beyond the classes in your jar, however, this is again impossible.
Class loading is a separate step preceding class initialization, and class initialization is the first point at which there is any opportunity to execute any code contained in your class. Thus, class loading is no longer ongoing when your classes first get a chance to inquire about anything. Moreover, classes are not necessarily loaded from jars at all, and in any event, they do not carry information about the source from which they were loaded.
I could perhaps go further afield with speculations about what you may mean to ask, but I don't see any interpretation of the question that affords an answer different from "no, it is not possible."
JARs don't load JARs. A jar file is just a meaningless container that helps holding class files and other resources together. Classes are loaded by a class loader(s) from a class path provided to the jvm. What you're trying to do is not possible. Maybe you can explain what you're trying to achieve and perhaps there is a better way to do it.
We have a process which needs to refer two different encryption classes having same name, at different times. Both the class names are same with same package path "com.abc.security.encryption".
Both the classes have same package name com.abc.security.encryption, however they are present in different jar files.
Let's say ENCRYPTION.class(new logic) present in Jar A and ENCRYPTION.class(old logic) is present in Jar B.
Now in my process, when we call Jar B API which refers ENCRYPTION.class, is referring to ENCRYPTION.class(new logic) present in Jar A instead of ENCRYPTION.class(old logic) present in Jar B .
Until I delete Jar A having ENCRYPTION.class(new logic), the ENCRYPTION.class(old logic) present in Jar B is not referred.
Since both the encryption logic are from different utility modules being used by many different modules, i am not able to ask them to change the name of the package.
I need a way to make sure both the logic are referred at required places without changing anything in those modules.
Can anything be changed in the class paths of my process or in the code, so that calling Jar B API, calls ENCRYPTION.class(old logic) present in Jar B itself. And when i call direct ENCRYPTION.class it should refer to ENCRYPTION.class(new logic) present in Jar A.
Tried by adding the first class path as "." for the process. But it did not solve the issue.
Your help is most appreciated.
Thanks,
Nvn
You should remove the problematic jar from the classpath. A classpath with multiple jars that contain the same fully qualified class names is a recipe for disaster.
If that's not an option, you might be able to create a custom class loader which does this swapping. But it probably won't be easy. There's a similar question about this which might get you started if you go down this road: Unloading classes in java?
In a web-app it is possible to have code in two places:
WEB-INF/lib
WEB-INF/classes
In the lib folder, code has to be packaged as a jar file whereas in the classes folder, code is in a folder structure that matches the package structure.
Are there any benefits to putting one's own code as a jar in WEB-INF/lib vs putting it as classes in WEB-INF/classes?
No, there is no benefit. There even are drawbacks:
creating the jar is one more task to do in your build
most (all?) of the app servers place the classes directory (or allow placing it) before the jars in the webapp's classpath. So placing your classes and resources there makes sure they are loaded in preference to a similar class or resources located in a jar of WEB-INF/lib.
JARs only exist to make it easier to move entire applications from one place to another.
That's very poor idea to paste your own classes inside a Jar unless you want to move/supply your code somewhere. Otherwise keep it as it is(WEB-INF/classes). That saves your time and it is standard recommendation.
Remember the other thing that each time you clean your project, new class files generates.
So, I have a Java project containing several packages (like com.myapp.a , com.myapp.b, com.myapp.c) for better readability and I want to build a jar to use as a library in another project.
But I just want to expose only some classes and interfaces from this jar. The problem is that if I don't declare these classes public then they can't be seen inside the jar file between the packages (for example I have a class A in com.myapp.a package that is used in com.myapp.b package).
So how can I expose just what I want outside of the jar when I have multiple packages defined inside?
Currently Java does not address this problem directly.
OSGi adresses this problem by explicitly defining the exported package list.
Also hopefully this will be addressed with the Java 8 Modularity system as well.
So one option is to use OSGi, but this option does not work if the jar file is used directly rather than as an OSGi bundle.
Another option is to use code obfuscation (like Proguard), to obfuscate the packages you do not want to expose.
Eclipse "solved" this problem by making all classes available, but classes that were not intended to be used by clients were placed in packages whose name contains "internal". For example, that might mean that you have packages named "com.myapp.b" and "com.myapp.internal.b". It's made clear to users of the classes that internal classes are not guaranteed to be upwardly compatible or even present in later releases.
Consider a scenario that a java program imports the classes from jar files. If the same class resides in two or more jar files there could be a problem.
In such scenarios what is the class that imported by the program? Is it the class
with the older timestamp??
What are the practices we can follow to avoid such complications.
Edit : This is an example. I have 2 jar files my1.jar and my2.jar. Both the files contain com.mycompany.CrazyWriter
By default, classes are loaded by the ClassLoader using the classpath which is searched in order.
If you have two implementations of the same class, the one the class loader finds first will be loaded.
If the classes are not actually the same class (same names but different methods), you'll get an exception when you try to use it.
You can load two classes with the same names in a single VM by using multiple class loaders. The OSGI framework can manage lots of the complexitites for you, making sure the correct version is loaded, etc.
First, I assume that you mean that the same class resides in two more jar files...
Now, answering your questions:
Which class is imported is dependent on your classloader and JVM. You cannot guarantee which class it will be, but in the normal classloader it will be the class from the first jar file on your classpath.
Don't put the same class into multiple jar files, or if you are trying to override system classes, use -bootclasspath.
Edit: To address one of the comments on this answer. I originally thought that sealing the jar would make a difference, since in theory it should not load two classes from the same package from different jar files. However, after some experimentation, I see that this assumption does not hold true, at least with the default security provider.
The ClassLoader is responsible for loading the Classes.
It scanns the ClassPath and loads the class that it found first.
If you have the same Jar twice on the ClassPath or if you have two Jars that contain two different versions of the same Class (that is com.packagename.Classname), the one that is found first is loaded.
Try to avoid having the same jar on the classpath twice.
Not sure what you meant by "the same class resides in two more classes"
if you meant inner/nested classes, there should be no problem since they are in different namespaces.
If you meant in two more JARs, as already answered, the order in the classpath is used.
How to avoid?
A package should be in only one JAR to avoid duplicated classes. If two classes have the same simple name, like java.util.Date and java.sql.Date, but are in different packages, they actually are different classes. You must use the fully qualified name, at aleast from one of the classes, to distinguish them.
If you have a problem finding out which version of a class is being used, then jwhich might be of use:
http://www.fullspan.com/proj/jwhich/index.html
If the same class resides in two more jars there should be a problem.
What do you mean exactly? Why should this be a problem?
In such scenarios what is the class that imported by the program? (Class with older timestamp??)
If a class exists in two JARs, the class will be loaded from the first JAR on the class path where it is found. Quoting Setting the class path (the quoted part applies to archive files too):
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.
In other words, if a specific order is required then just enumerate the JAR files explicitly in the class path. This is something commonly used by application server vendors: to patch specific class(es) of a product, you put a JAR (e.g. CR1234.jar) containing patched class(es) on the class path before the main JAR (say weblogic.jar).
What are the practices we can follow to avoid such complications.
Well, the obvious answer is don't do it (or only on purpose like in the sample given above).