We have a strange issue with two Java processes A and B that load same jar file (not 2 separate copies of the same file, same actual file).
The .jar file was replaced with a newer copy (renamed to .jar_BACK, then a new file copied in its pace), and one of the Java processes (A) restarted. The other Java process (B) keeps running.
Process A works without any problem.
Process B gets a NoClassDefFoundError exception when it tries to use one of the classes in the .jar we updated. The class that is "not found" was already loaded before.
Investigation by jar -x shows that the "missing" class is actually present in both old and new version of the jar, in same location within the jar file and the class itself is unchanged.
Restarting process B fixes the problem.
What would be causing such a behavior?
My current guess is: either standard Java classloader or some custom classloader invalidates classes from a jar when .jar file is renamed. But I can't find any documentation on this, and my previous understanding was that classloader does not do any such monitoring for missing .jar files.
I am pretty sure we're not using any custom class loaders, either.
Java version: 1.8.0_144-b01
OS version: Red Hat Enterprise Linux Server release 6.9 (Santiago)
I believe your issue stems from the fact that you changed the name of the jar and tried to overwrite it with a different file of the same name. The JVM doesn't like that. This question probably has the details you want.
Related
I'm very new to java. I'm developing a tool that checks if your PC meets some set of specifications. This included writing and executing a separate batch file and including an API (SIGAR) for the model of the CPU.
My problem is that when I tried exporting it to Runnable JAR in eclipse, and I ran the resulting JAR from command line, it gave me lots of 'DLL not included in build path' Exceptions. After including the folder that contains the API DLL in the build path, I got similar exceptions. The thing that fixed it was adding the folder containing the DLL to environment variables (PATH) in Advanced System Settings.
Questions:
The JAR now works fine on my computer, but what about the users who download the JAR? Will they also need to add the DLL to environment variables? If so is there a way the JAR can do that for them?
My JAR won't run with a double-click, but will run from command line. Is there any way around this that will carry over to users who download the JAR too?
If the user downloads the tool and can't run it because they don't have the right version of the JRE, will the tool notify them? If not, is there a way around the user having to update JRE or will wrapping as an EXE suffice?
Thanks in advance, much appreciated. Lots of questions.
Q1: The JAR now works fine on my computer, but what about the users
who download the JAR? Will they also need to add the DLL to
environment variables? If so is there a way the JAR can do that for
them?
You can put a DLL inside a JAR file:
How to make a JAR file that includes DLL files? (Hint: read both answers ... completely.)
However, when you distribute a JAR containing a DLL, you then have the problem that different platforms require different DLLs (or whatever). Even with Windows you have the problem of 32 bit versus 64 bit DLLs.
Q2: My JAR won't run with a double-click, but will run from command
line. Is there any way around this that will carry over to users who
download the JAR too?
You cannot address that problem in a JAR file. The "double-click to run" functionality is implemented by the OS. The best way to provide this kind of functionality is using (platform specific) wrapper scripts that are double-clickable.
Q3: If the user downloads the tool and can't run it because they don't
have the right version of the JRE, will the tool notify them? If not,
is there a way around the user having to update JRE or will wrapping
as an EXE suffice?
Unless you have a JRE installed, the JAR file is just a passive blob of data. (A ZIP file, actually).
If the user has a JRE that is too old, then either the JRE will be unable to load any classes in the JAR (because the classfile version number is wrong), or you will get errors because of missing (system) classes.
The only hope would to do something like providing a wrapper script to launch your application that checked the JRE version before attempting to launch the JAR.
As a general rule, if you want to do fancy stuff like this you need to distribute your program in an installer, not as a bare JAR file.
jwrapper manipulates application jars somehow, and is resulting in a non-functioning jar: at runtime it throws a "MyClass cannot be cast to MyClass" type error. I believe this is caused by re-evaluating code that creates a class loader, leading to multiple instances of class MyClass being loaded.
The jwrapper docs don't describe the changes made to the jar, except for the use of pack200. I've tested pack200 in isolation, and it does not cause this problem.
I've also tested the jar built by jwrapper without using the wrapper executable, by passing it to "java -jar". So it's not jvm transmuting, or anything else that the wrapper is doing: the jar itself is broken.
UPDATE:
jwrapper allows skipping pack200, but then the install file is huge. Since pack200 works when run standalone, I could work around this if there were some way to tell jwrapper that the file is already packed. Using <Pack200Exceptions> doesn't help, because then it doesn't know the file is packed.
The underlying problem is that jwrapper sets the pack200 option "modification_time" to "latest", which changes the modification times of all the class files. At run-time this causes the clojure compiler to attempt to recompile the classes from source.
A work-around is to remove the .clj files from the jar prior to packaging, preventing the compiler from running. The lein ":omit-source" option is not sufficient here, because it leaves in .clj files from any dependencies. Instead you must use a pattern in :uberjar-exclusions, e.g.
:uberjar-exclusions [#".(clj|java)"]
as detailed here:
https://github.com/technomancy/leiningen/issues/1357
I am trying to run a java program that uses protobuf.jar, but I keep getting this error.
I have set my classpath variable in linux so that:
CLASSPATH=/home/.../src/PlaceServer.class:/home/.../src/protobuf.jar:/home/.../src
export CLASSPATH
But then when I run my program in the command line after reading in the jar.
java PServer
I get this:
java.lang.NoClassDefFoundError: com/google/protobuf/MessageOrBuilder
However when I run another program that also reads the same jar, this one called BServer
java BServer
It works fine, and correctly as I want it. I even tried running under these commands instead
java -cp .protobuf.jar PServer
And it still did not work for PServer.
However, if I run the same programs on my Macbook from the command line (also in Eclipse in either OS) I do not get this Exception and it all works fine.
Thanks for your help!
There are a couple of things to check to get rid of this error:
Verify that all required Java classes are included in the application’s classpath. The most common mistake is not to include all the necessary classes, before starting to execute a Java application that has dependencies on some external libraries.
The classpath of the application is correct, but the Classpath environment variable is overridden before the application’s execution
When you run the application in Eclipse, the IDE resolves this by using the .classpath file inside the project folder. When you build an application (create the jar), you could accidentally omit this class, or change its location.
What you need to do is to first open the jar, and make sure that the class in question is in fact inside the jar, in the same path. Then, go through the list above.
I have a java process running with two jar files in the classpath namely
- A.jar
- B.jar
While the process was running, I replaced the B.jar with another B.jar which I updated with some files. Now in my process, I see some ClassNotFoundException s for the classes in the B.jar. I don't understand what is happening here. I thought the jars would be loaded when the java process was started. If that is the case why is it happening? Can somebody help me with this?? I know if I restart the process, everything will be fine. But I am curious to know the reason behind this.
Classes in a JAR file are loaded when they're first used, not at JVM startup. By replacing B.jar while the application is running, if you've removed classes that are referred to by others, you will get a ClassNotFoundException.
This can also happen in Java 7 if a class that you haven't used for a while has been garbage collected. The JVM will attempt to re-load it, and find that it is no longer in the classpath. This can also happen in earlier versions of Java if you're using the -XX:+CMSClassUnloadingEnabled startup option.
JVM supports static and dynamic loading of classes. JVM will load at startup all classes that are linked explicitely, but won't "discover" classes that are loaded dynamically at Runtime, via reflection for example. If you're doing a Class.forName("org.package.mySuperClass") in your code, and if your SuperClass is never linked by other pieces of code, it will be loaded at call time. If your jar containing this class has been removed from the classpath before the call, a ClassNotFoundException will be thrown
Note that a lot of modern framework use dynamic loading (even dynamic compilation that links to classes in classpath that were not linked before), and it's diffcult (and uncertain) to know which ones.
ok I have been search the internet all day and I have tried everything I have seen so i am wondering if someone could help.
I have a class that references a jar file which i have copied into my workspace in the lib folder the jar is: Classifier4J.jar, My class run perfectly on the console no error at all. When I try to package the .jar together and run the .jar from another program it gives me this error:
Exception in thread "pool-2-thread-1" java.lang.NoClassDefFoundError: net/sf/classifier4J/bayesian/IWordsDataSource...(10 more)
So clearly when i create the .jar its not able to reference the classifier4j library that it needs.
Things I have tried:
-Configure my build path
-Change the manifest file
-packaged the .jar with my .jar
-in eclipse went to file>export>java>runnable .jar then references the libraries
and many other things and nothing worked.
If anyway has had a similar issue or knows why this is happening could you please help me its really wrecking my head. Is this a problem with eclipse? can it be done through eclipse?
Thanks in advance
Jay
Ok after hours of looking at the problem i finally found the solution, When I output my classes as a .jar file I pointed it to my manifest file. I couldn't find what i was doing wrong because it didn't work. I decided to look at what was actually put into the jar and i saw that eclipse wasn't putting the correct manifest file i asked it to into the jar. It was putting a new one that looked like:
Manifest-Version: 1.0
where it should have looked like:
Manifest-Version: 1.0
Class-Path: ../lib/Classifier4J.jar
where the lib folder was back a directory from where my jar was. Everything is working perfectly now. Thank you for all your help.
Jay
It is hard to understand what you are actually trying to do, what you have actually tried, and what you expect to work, but I think the clue is here:
packaged the .jar with my .jar
This seems to imply that you are trying to create an "executable JAR" that contains all of the dependent JARs ... as-is. That won't work, an "executable JAR" cannot contain other JARs. (Well is can ... but you can't put them on your application's classpath without doing seriously complicated things.)
Basically, you have two options:
Don't try to include the dependent JARs in your (executable or not) application JAR. Keep them separate, and configure the execution classpath to include them. (And beware that for an executable JAR, the execution classpath must be configured as a Manifest entry. If you use java -jar ..., the -cp argument and $CLASS_PATH are ignored!)
Create a so-called "uber-JAR" by exploding the application JAR and all of the dependent JARs into a single directory and then creating a single JAR (with a suitable Manifest) from the lot. Your build tool or IDE may have support for this via some plugin.
I personally prefer the former approach ... combined with an "installation directory" for the application and a wrapper script. With the latter approach you make it hard for the user (or deployer) to mix-and-match versions of dependent JARs. Furthermore, the "uber-JAR" approach could conflict with a 3rd-party library's license.
At last, maybe we can get somewhere
.... the .jar file is ran from a tomcat application its a simple adapter for log files that all.
OK. You should have mentioned that before, because it is a critical piece of information. In order for a JAR file to usable within in a web container (i.e. in the same JVM as Tomcat), there must be a copy of the JAR file and all of its dependent JAR files in the web container's directory tree. There are two choices. Either you put them in the webapp's lib directory (i.e. webapp/WEB-INFO/lib) or you put them in the shared library directory ... which depends on which version of Tomcat you are using.
(The "executable JAR" approach won't work here. The classpath stuff you put in the Manifest is irrelevant. And nesting JAR files won't work either.)
The particular library is not included in your jar. You could either try to fix your eclipses build configuration to include that library, or add the library's jar to the classpath when you run the program.
The later may be easier. Just add the following to the command when you execute your program.
java -cp yourjar.jar;thelibrarysjar.jar com.your.Mainclass
You need to understand that NoClassDefFoundError hardly ever means that the identified class can't be found. Far more often, the class was found, but something prevented it from being successfully loaded.
The two most likely problems are -
Some other missing class that prevents the named class from being "verified".
Some incompatibility due to a different version of a class (usually another class) being used in compilation vs execution.
In your case it's most likely that when you have the problem you're picking up a different (and incompatible) version of some other jar, vs the environment during compilation.