Which jar file is used during compilation? - java

Let's assume I have two jar files on classpath when building my project - myJarFile.jar and myJarFileOld.jar. They contain the same packages and the same classes, but the myJarFileOld.jar contains old implementation, which causes that the compilation fails. I'm not asking for solution of this error, I know that I should remove myJarFileOld.jar to make compilation work. However I'd like to know, what mechanism decides which class from which jar file is used during compilation, when both jar files are present?

When a class needs to be loaded, all jar files in the classpath, in order, are scanned. As soon as the class is found, it's loaded.

Not fully sure, but I believe the order of classpath appearance is deciding. If it's found in first jar, then it's not search in another. However I'm pretty sure that class loader will load both jars at the beginning, and you will get some errors about duplicate code. However I'm not sure this, this is probably related to runtime environment.

you have this feature in Eclipse where you can specify the ordering of the jars that you want to be executed from the project classpath.Go to
Project->Select Properties->Select Build Path from left pane-> go to Order and Export Tab->Select Top or Bottom button-> click ok.
The next time you build your project the jar from the classpath will be picked in the order that you have specified.

Related

Strange behavior with different java application export option

I have java server application wich uses many libs (netty, guava, etc). I always export this application as one single .jar. When I run application in Eclipse, I didn't have any problems. But if I start app in console (Windows, or Ubuntu, doesn't matter), I have strange problem: ALL connection processes via sockets last toooo long. For example, simple http connection via HttpAsync or others (rabbitmq connection, etc.) lasts 1-2 min. But after connection completed, data sends/receives fast. I can't figure what the problem. As mentioned before, I use Eclipse for development.
As you know, you can export project 3 dif ways (in Eclipse):
Extract required libraries into JAR.
Package required libraries into JAR.
Copy required libraries into sub folder next to JAR.
So, when I used 2 option, I had problem. When I switched to 3d option (all .jars in folder near main .jar), problem was solved.
Generally there are no big difference between 2 and 3 option (in 2 all .jars just inside one jar). I thought that it was cause of extra time needed to load new classes in execution time from the jars. But problem occurs not only at start, but for all new connections.
Can someone explain this behavior?
UPD: Eclipse Luna. Doesn't matter what OS I'm using (Windows, or Ubuntu), even doesn't matter what jvm (tried with different Oracle jdk, even tried open jdk).
This all talks about difference in performance when packaging into JAR v/s extracting into JAR & difference in performance when running from Eclipse v/s running from console.
Difference in performance when packaging into JAR v/s extracting into JAR:
Extract required libraries into JAR:
What it does:
In this option Eclipse will extract all the classes from the referenced JARs and package into the generated JAR.
If you open the JAR then you will find that there are NO referenced JARs packaged but all the classes of referenced JARs are arranged as per the package structure and then packaged inside the JAR at root level. This brings the key difference in performance as compared to the "Packaging required libraries into a jar file" where there is additionally cost of runtime parsing and loading of JAR in memory etc..
When exporting as JAR through Eclipse then it is best option if performance is concern. Also this is scalable option because you can ship this JAR
MANIFEST.MF Main thing to note in this file is you main class. When you run the JAR you are directly running the class you need.
Main-Class: com.my.jar.TestSSL
Package required libraries into JAR:
What it does:
In this option Eclipse will:
package all the referenced JARs into the generated JAR.
employ Eclipse's JAR loading mechanism through org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader and you can also see org.eclipse.jdt.internal.jarinjarloader package into your generated JAR and this package is just under the root directory of the generated JAR.
Now of course this is the additional cost which comes when you choose this option because when you run the JAR then it is not you main class getting executed but JarRsrcLoader will be executed which will load your main class and other libraries, and all the referenced libraries are packaged. See MANIFEST.MF section below
MANIFEST.MF Main thing to note in this file is you main class. When you run the JAR, JarRsrcLoader will run and will do further job.
Rsrc-Main-Class: com.cgi.tmi.TestSSL
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
Now for last Eclipse export option - "Copy required libraries into sub folder next to JAR", I don't think it is a very scalable solution to consider because this imposes your file system dependency, so I would say don't do it.
Difference in performance when running from Eclipse v/s running from console:
When you run application from Eclipse then it is quiet similar to 1st export option where Eclipse doesn't need to parse and load JARs at runtime and all.
This is however a very trivial point, key is the consideration of Eclipse JAR export option 1 v/s option 2.
Final words:
Use "Extract required libraries into JAR" for exporting JAR and you will see substantial performance gain.
It is highly improbable that your socket connections are lasting long when you run from console because JVM runs code then it would have same or very comparable performance when running from Eclipse and console (considering same Java versions in both case). You could be feeling because of packaged JAR performance. Try extracted JAR and you should be fine.
Also, consider the amount of logging you are doing. When running through, depending upon configuration Eclipse may mask a lot of logging and hence saving you i/o time.
Do understand how classes are accessed from JAR class path, which is like additional computational cost when you are referencing classes from JAR.
As we don't know the exact structure of your JAR here is a more general explanation (assumed you run your application with java -jar your_app.jar).
case Copy required libraries into sub folder next to JAR.
if a class needs to be loaded the class loader (after the runtime JAR) first checks your_app.jar to find a required class
if the class is not found it traversed over all JAR files in the subfolder
all JAR files could be kept in the filesystem cache for further reading
case Package required libraries into JAR
if a class needs to be loaded the Eclipse class loader JarRsrcLoader (after the runtime JAR) first checks your_app.jar to find a required class
if the class is not found it traversed over all embedded JAR files, which means as first they need to be decompressed from your_app.jar before the content can be read
the extracted embedded JAR files are not kept in the filesystem cache for further reading (as they are not files in the filesystem)
If you have a bigger number of hugh embedded library JARs this might lead in a slow down of class loading (but only for the first time a class is loaded by a class loader).
You can see the difference in the class loading if you compare the outpout of
java -verbose:class -jar your_app_external_library_jars.jar
with
java -verbose:class -jar your_app_embedded_library_jars.jar
The performance might be improved by generating an INDEX.LIST file for each JAR file (e.g. your_app.jar and the embedded library JARs).
It happens because when you go with "uber jar" approach, some metadata might be lost.
It's just an example, but if you download this and this, take a look inside the jar. There are a few files with the same name in the same META-INF folder.
Those files might be important, and when eclipse repackages things for you, he might not be the doing a decent job on merging such files.
That is what might be happening to you.
In the 2nd approach, You have all dependency jars in the main.jar.
So it won't load any of the dependency jars unless required.
Whereas, in case of 3rd option, your main.jar and other dependency jars are independent (unlike 2nd way), and hence gets loaded for connections and is available.
try adding a log statement or syso by manipulating a dependency jar to see this working.

JAVA ClassLoad same class name

Yesterday i thought one question ,below is the detail:
I have 3 JAR files, a.jar, b.jar ,c.jar . both these jars files have a class named com.test.Test ,and sayHello() was defined in this class.
I create a web application, i reference a.jar,b.jar,c.jar . And in main method, i involve sayHello(); .at this time, which com.test.Test will be load?
the result is a.jar.
any body tell me the reason ?? thanks in advance!!!
That is what java language specification says. It loads what ever the class first occurs in classpath and ignores other.
Instead of focusing on which one will be loaded, realize that the stuff within the JAR files probably need their com.test.Test class instead of someone else's com.test.Test to work properly. That means for a functional system you'll have to make a way that a.jar finds a.jar's com.test.Test instead of the one in b.jar. The same goes for b.jar finding it's classes in preference to a.jar's.
The only way to do this is to use a framework which adds name spacing beyond the java package mechanism. This is typically done with multiple classloaders, often one for each JAR file. You can write such a thing yourself (Tomcat did), where you need to specify the rules for cross-loader discovery, or use something akin to a OSGi framework.
Whichever Jar File comes first in your classpath will be used..
You can modify your CLASSPATH environment variable to the path of your Jar file
Suppose you modify it as below: -
set CLASSPATH = %CLASSPATH%;.;a.jar;b.jar
then a.jar will be used..
You can also modify it by: -
set CLASSPATH = %CLASSPATH%;.;b.jar;a.jar
In this case, b.jar will be used..
These commands you need to run from your Command Line..
** NOTE: - If you are using any IDE, then they don't use System Classpath.. You need to set different classpath for the IDE you are using..
If you are using an IDE, such as eclipse, you can modify your classpath on the properties of the project, then go to Build Path, and then you have the Order and Export tab where you can move up and down the jars. The one of the top will be the first taken by your application.
This you can also do manually by editing the file called "classpath" which is on your project and move to the top the jar you want your application to use first.

How to give priority to a jar file in eclipse classpath?

There is a class file TagAttributeInfo which is under two of my jar files.
j2ee-6.1.jar,servlet-api-2.3.jar .
Now i need that class to be called from j2ee-6.1.jar but in runtime ,it is taking the class of servlet-api-2.3.jar,which i don't want.
Can anybody help me to prioritize the jar file in class path so that i will give first priority to j2ee-6.1.jar.
You move it up in the classpath order (right click on the project, select Build Path, followed by Configure Build Path and then Order and Export)
Go into your Project Properties > Build Path.
In the Order and Export tab, find j2ee-6.1.jar and press the Up button to move it higher up in the class path order. Keep pressing Up until it is above servlet-api-2.3.jar.
Finally press OK.
The first jar that it finds on the classpath that has the class it's looking for will be used. So you can prioritize your class by putting its jar at the beginning of the classpath.
How exactly you would do this in Eclipse I'm not sure, but there's probably some list of project dependencies that correspond to the jars on the classpath, and hopefully you can order the elements on that list and put your jar on the top. Try looking for it in the project settings panel.
remove servlet-api-2.3.jar, you don't need it.
or move j2ee-6.1.jar higher up than servlet-api-2.3.jar in the lib list.
I don't know if it's part of the spec but placing the "higher" priority jar earlier on the classpath works when I've done this in the past.
Are you sure you need both jar files?
Anyway, in the Build Path dialog you can specify the order for jar files to be searched.
Only one of these jars should be in your classpath, to be able to compile your classes. But at runtime, the jar will be in your application server classpath, and you won't be able to replace it with the other one.
If your container is a JEE6 container, then it uses servlet 3.0 and the servlet-api-2.3.jar is obsolete and should not be in the classpath. If your container only supports servlet api 2.3, then you won't be able to use classes from the jee6 jar in this container.

How to use jardesc in Eclipse?

I want to create an executable JAR-file from my eclipse project. It references other projects, which also reference other projects (and so on) and some JAR files.
According to this answer, everything should go "within 2 clicks". But not for me:
It reports a warning:
Problem writing mg/build/classes/META-INF/MANIFEST.MF to JAR: duplicate entry: META-INF/MANIFEST.MF duplicate entry: META-INF/MANIFEST.MF
It looks like it tries to include existing manifests from all projects, which simply doesn't make sense. I actually do not want to include any of them, just to generate a new one. I haven't found any way to switch it off. OK, it's just a warning.
It looks like I need to manually take care of all the referenced jar files... actually, Eclipse knows them, I do not.
I need to either include the content of all the referenced jar files or to copy all of them into the target folder and list them in the Manifest. I've got no idea how to do it.
I also wonder if the jardesc file is usable from ant build scripts.
I got the same error message ("duplicate entry") when, in my case
I checked the option to include an existing manifest file.
AND I specified to include MANIFEST.MF among the files to be included in the .jar.
Once I excluded MANIFEST.MF in the "Select the resources to export:" file tree of the jar generation wizard, the warning disappeared.
As for including referenced projects with your jar, I recommend making them into jar files as well, and including those in the project you want to make a jar of.
Then, make sure to set the manifest's classpath correctly.

Adding external .jar file in Eclipse

I'm having trouble adding a .jar file I downloaded for my Java project. This is really the first time I've used eclipse, so please bear with me and for some reason (I have no clue why), I just find it somewhat confusing.
I know that in order reference different class files you simply need to create a class library and add it to the build path. From there, all which needs to be done (unless I'm misunderstanding this for whatever reason) is use the "import" keyword to import whatever .jar, .java, or .class/.interface file necessary into the project.
I've tried that with my .jar. I have it referenced in the build path (all I did was just copy the jar to the project directory, and then use the build path option to add it externally), but when ever try to call the object "Delegator", which obviously is a part of the .jar file, it won't read.
Am I missing something here? Seriously, anyone who knows the answer to this - you're relieving a mother of a headache. And before anyone asks - yes, I've searched this one to death. I've found similar questions, but nothing which quite hit what I was looking for. Either that, or I really just lack the common sense.
Right click on project->BuildPath->Libraries->Addexternaljar and then press ok and if it doesnot worked then you should go to the Order and Export tab and checked the jar you have just added in your project. It will solved your problem.
There are several possible reasons, for the question hasn't mentioned the specific failure, and where it has occurred. The following is a list of possible reasons I could think of, but this may not be exhaustive:
You can import a class, in a different package only if the class is public. The only exception is when you are using the class in the same package. If the class is an inner class marked as private, then you're well and truly out of luck. The Delegator class in question might not be public, and that's why you may be unable to use it. This issue ought to be caught by the compiler.
The directory structure within the JAR might not match your package import statements in your classes. This might not be necessary, for Eclipse ought to provide possible fixes, but it is better to verify that nevertheless. Again, the compiler should complain if this is the case.
If the issue is at runtime, then, it is most likely that the JAR is not available in the runtime classpath. You'll need to configure the Runtime configuration, to add the JAR to the runtime classpath. Refer to the Eclipse documentation on run configurations, if you need to know how to change the runtime classpath.
Note:
Exporting the build classpath entries would matter to other projects that depend on the pertinent project; unexported entries will have to be re-imported if required in other projects. This would not apply to a run configuration.
Update
Every Java application needs a main(String[] args] method to start execution. This is the entrypoint for the application. From the comment, it appears that the main method is in a different class. If so, the said class ought to be used to start the application. In Eclipse, a "Run configuration" might be used for the class that lacks this entrypoint, resulting in the described error. One can rectify this by creating a new Run configuration for the class with the said entrypoint. This may be done by one of the following:
editing the existing Run configuration to use the desired Class (the one with the main method). See the above link, in the third bullet point. Edit the value of the class to be launched.
creating a new Run configuration for the desired Class. Usually, you'll need to traverse to the desired class, and run your application (using the Alt+Shift+X+J shortcut) from the said class.
i was facing similar issue with spring jar files but then tried with different jar files and it work so I think , classes defined in jar files were private and not available outside of jar hence you were not able to access the file .
thanks ,
Raju Rathi
Right click on the project--->Build Path--->Configure Build Path...--->In left side you have to choose Java Build Path--->Libraries--->Add External JARs--->ok--->ok
Steps to add jar file in eclipse
1. right click on project
2. click on Bulid Path->configure path
3. click on java Build path
4. Click on libraries tab
5. click on add external jar tab
6. choose jar file
7 click on ok
Copy the .jar file in libs folder which you want to add in your project.
Right click on .jar file -> Add Build Path
Done.

Categories