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.
Related
I have a class called Passthru, which extends a class called Component, which is held in a jar file. At run time, I want to load Passthru (using URLClassLoader with one or more jar files, plus optionally a local directory), and then test the superclass against a few possibilities, of which one is Component. This works great if both Passthru and Component are in the same jar file, even if different packages, but if Passthru is in a local directory (with Component in the jar file, as before), doing getSuperClass on Passthru returns Object. I know Object is in Passthru's parent tree, but in this case I don't seem to be able to find out Passthru's direct parent. I suppose I could go to Passthru's source and scan to find the superclass, but there should be a simpler way...?!
I also noted that in both cases Passthru is loaded with my URLClassLoader, but when Passthru is in the local directory, its parent's loader shows as the "primordial" loader (null) - don't know if this is relevant...
Solved!! It was sort of a dumb question: it turned out that the source code in the directory was back level, and didn't match the code in the jar file. Also, I hadn't added the directory code as an Eclipse project, so apparently Eclipse compiled this code but didn't give me any error indications, so I didn't see the compile errors "behind the scenes". So when I worked purely in the jar file, everything worked, but when I went to the directory, compile errors prevented the classes there from picking up the Component class. Thanks to the people who responded to my question - sorry if I wasted your time!
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?
The Java standard/system libraries (java.*, javax.*, etc.) are stored in lib/rt.jar inside each JRE distribution.
Say I have an application that I have compiled and jarred into myapp.jar. This JAR only contains my app's class files, and merely references system classes like System, File, Runtime, Thread, String, Boolean, etc.
So when I run my app, say via java -jar myapp.jar, the JVM is obviously doing so last minute linking (or something) where it is executing the bytecode of my class files (inside myapp.jar) and then "jumping" into lib/rt.jar to run bytecode located there. I would imagine the process is the same if myapp.jar depends on other JARs provided on the runtime classpath.
My question is: what is this "linking" process called, and how does it essentially work?
That rt.jar is part of the bootstrap classpath, a parent of the usual classpath you already know and that you configure when you use the -cp option (you can actually change the bootstrap classpath too using the -Xbootclasspath option to load, for example, a custom Java runtime).
See Oracle documentation for a detailed description of how classes are searched/loaded from the system defined classpaths hierarchy.
Now, the additional questions you seemed to have:
How is the archive actually found?
It's simply hardcoded. If the java binary is located in <common_root>/bin/java, rt.jar will be searched in <common_root>/lib/rt.jar.
How is the "linking" performed?
On the JVM there is no actual linking, the classes are dynamically loaded using a mechanism based on a hierarchy of ClassLoader that are the software components that actually do the class file loading/parsing. When you try to load a class, the search starts from the application-facing default classloader(or a child classloader if you have defined one) and if the class cannot be loaded the loading attempt is repeated with a parent classloader until the bootstrap classloader is reached.
If the class is found, the .class file is loaded, parsed and internal structures representing the class and its data are created.Once the class is loaded a new instance can be created.
If instead, even the boot classloader could not load your class a user-visible ClassNotFoundException is thrown.
I use this code snippet to obtain a file as an input stream. The file version.txt is packaged in my app's jar, in the upper-most folder.
InputStream resource = getClass().getClassLoader().getResourceAsStream("version.txt");
This works almost all of the time. But for one user, it's picking up another version.txt, that's not in my jar. How can I ensure that this loads the specific version.txt file that is in my jar?
When you say, "upper-most folder", you mean the default package? If that's the case, you would have to ensure your JAR is earlier on the classpath than whatever is contributing the other version.txt file as Java searches along the classpath until it finds the first match.
Since it's hard to ensure your JAR would always be first, you should really place the version.txt file in a non-default package, such as:
com.yourcompany.yourproject.version
And then you'd need to modify the code to locate it:
Stream resource = getClass().getClassLoader().getResourceAsStream("com/yourcompany/yourproject/version/version.txt");
Using the default package is an anti-pattern.
This is the danger inherent in putting things in the top-level package, you can pick up things you didn't really want to. This can be a benefit (like with log4j config, for example), but it's usually not what you want.
I strongly recommend putting your version.txt within your application's package structure, e.g. in com.myapp (or whatever your package name is), and then load it from there using
getClass().getClassLoader().getResourceAsStream("com/myapp/version.txt");
the code snippet will use the class loader that loaded this class to find the version.txt file.
if the file exists in the classpath used by the classloader in more than one place, it may return the wrong file (depending on the classpath order).
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).