jar built with jwrapper doesn't work - java

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

Related

How Maven loads classes using ClassLoader

My knowledge of ClassLoader in Java at the moment, is a little obscure. That's because I have not found any good documentation geared towards beginner of CLassLoader, thus far. And what I am looking for exactly is in relation to Maven. With disclaimer stated, let me get into my question.
I am writing a Spring MVC application, and I decided to look into how the Dependencies - Jars and classes are loaded using ClassLoader. And what I found is according to the documentation of ClassLoader, classes in Jar are loaded from CLASSPATH, and I can see them under the .m2/repository directory, but CLASSPATH does not yield anything, it's practically empty.
Can somebody please explain to me, how the classes from JAR are loaded into JVM Memory using ClassLoader using Maven, if the CLASSPATH is empty.
Thanks
You're confusing a few things.
The ClassLoader
The ClassLoader is a runtime concept. Maven is a compile time concept. Therefore, one has nothing whatsoever to do with the other. Maven and ClassLoaders do not interact. At all.
When you start a basic java app (java -jar foo.jar or java com.foo.MainClass), you get 2 classloaders. One loader will load system stuff: java.lang.String, for example. the executable itself 'just knows' how to do this (you don't need to configure PATH, CLASSPATH or JAVA_HOME - it just works); up to java 8, it finds rt.jar automatically, which contains String.class and other core classes. Starting from java 11, it finds the jmod files in your java distro.
Then, once the VM has 'booted up', the VM makes another classloader, also based on its built-in stuff: The app classloader.
This one uses 'the classpath'. The source of this depends on how you ran your java app:
java -jar somejar.jar
The source of the classpath in this case is the Class-Path: line in the jar's manifest (the file at META-INF/MANIFEST.MF). And nothing else - the CLASSPATH environment variable, and any -cp or -classpath options are entirely ignored.
java -cp a.jar:b.jar:. com.foo.ClassName
Note that -cp is short for -classpath (they mean the same thing): Here, the classpath is taken to be all the files and directories listed (on windows, use ; as separator instead), and classes are loaded from there. Including com.foo.ClassName itself.
java com.foo.ClassName
If you don't explicitly specify a -cp param, then the environment var CLASSPATH is used. You don't want this though. Always specify classpath.
That's runtime - maven has nothing whatsoever to do with this.
Make your own
You can make your own ClassLoader; the abstraction is such that all it needs to be able to do, is turn a resource name into bytes. That's it. You can make a ClassLoader (literally! public class MyLoader extends java.lang.ClassLoader { ... }) that.. loads data from a network, or generates it on the fly, or fetches it from an encrypted data store. Whatever you like.
Using custom classloaders like this is a solution for finding classes in 'weird' places (not jar files or directories), as well as a mechanism to allow java to 'reload classes' on the fly - very useful when developing, say, web apps, without the use of a hot-code-replacing debugger like eclipse has. ClassLoaders is a mechanism whereby a web server can have the following feature: "I load jars or wars from a certain preconfigured directory.. and if you replace the jar, I will see it and start using the new one".
Writing your own ClassLoader is bordering on rocket science and not usually required unless you're, say, writing an app server. Not a common job.
Maven
To compile source code, the compiler must know the methods and fields and such of all the types you refer. You can't compile "Hello".toLowerCase(); if the compiler doesn't know what String contains.
Thus, compilers also have this notion of 'I need to find classes'. But This is not called class loading, and notably, maven never loads any classes. If it did, any static initializers in any class would run, and mess up your compile. Maven instead just inspects the class file, never letting the VM actually load it, to know what kind of methods and fields and the like are on offer.
java.lang.ClassLoader plays no part in this.
javac itself has a -classpath option as well. So does maven, really.
Maven constructs the classpath automatically, by putting the stuff it already compiled for you (e.g. when compiling the stuff in src/test/java, the compiled stuff from src/main/java is on the classpath), as well as all the dependencies. How? Well, does it matter? Maven does. It constructs a large list of dirs and jars and passes it to javac via the -classpath parameter.

NoClassDefFoundError exception after .jar file moved away

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.

Compiling a single class file edited from existing jar with no dependencies

I need to edit a single class file from within a jar.
I have successfully extracted the class file from the jar, and I have decompiled it and found the logic I need to change.
However, I'm unable to recompile this class file, because it imports libraries I don't have and don't know where to get (netbeans and iharder).
The needed files should all be within the jar, right? Can I use the jar for this purpose?
I do not understand much of Java's overarching syntax, so anything related to packages or jar file structure might go over my head...
The JVM just needs to be able to find the dependencies at runtime. Often, they'll be installed in a standard location (the classpath), rather than being bundled with the jar that uses them. However, you could theoretically even do something like download dependencies at runtime and load them via a classloader.
Apart from that, decompiling and recompiling is often not a good idea, since decompilation is a lossy and error prone process. It generally only works in simple cases, and has limitations, as you've discovered.
If you understand Java bytecode, you can edit the class by disassembling it with Krakatau, editing the .j file, and then reassembling. This allows you to edit any classfile without needing to compile, meaning you don't need the dependencies. It also works no matter how complicated the class is, and even works on obfuscated classes.

NoClassDefFoundError when referencing library

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.

Compiling Java & JARs

I just asked a recent question about distributing executable JARs and their dependencies, and it made me realize that my understanding of JARs may be fundamentally flawed.
Thus, some might say "Hey now! This here is a duplicate question!" But I say nay, this question is a completely separate offshoot of this
original question, and is concerned with Java fundamentals!
If I have an application that depends on, say, the Apache Commons CLI as well as JODA Time, and I pack this app up into a distributable JAR, my original question was: Without including the CLI and JODA JARs in my JAR, how does the program run on the client-side???
I am now thinking that since my code, which uses CLI and JODA, gets compiled into classfiles, and that bytecode is what gets packaged, then there is no need to include CLI or JODA (or any other 3rd party JAR) in my JAR, since it is all now functioning bytecode.
Can someone confirm or correct me? This revelation, though late in coming, has been staggering.
No, that is not quite right. The key to everything is the classpath. Is all of the compiled code and/or other resources on the classpath? If you package everything up in one single jar, then yes, it is in the classpath and the JVM will locate all the resources to run. Otherwise, you need to specify (with a .bat or .sh file or something) all the resources that your application is dependent on, so the JVM will be able to appropriately look for those resources (be they Java code or properties files or whatever).
Also if I am reading your question right, are you assuming that the CLI and JODA code gets compiled into your code? If so, I hate to burst your bubble, but that is not the case. When your code compiles, it does not bring in dependencies (not in the sense you may be thinking). What it does at a conceptual level (correct me if I'm wrong JVM gurus) is it references other classes. Those references are what you are building when you code a class and compile it. At runtime the JVM will attempt to locate the compiled class behind the reference and THAT is where you either need the jar with those classes in the classpath OR you need those classes in your executable jar.
Make sense?
The third party libraries (JodaTime, for example) need to be on the classpath during runtime. Not "packaged within your JAR".
If your app is launched from a JAR. You should specify the classpath in the manifest file which is packaged within the jar - http://download.oracle.com/javase/tutorial/deployment/jar/downman.html
You can have ANT generate the manifest classpath for you using the manifestclasspath element - http://ant.apache.org/manual/Tasks/manifestclasspath.html

Categories

Resources