run with difference jar? - java

I am from .net world. I remember .net will immediately complain if you build with one dll but supply a different dll at run time.
I am now adding some hadoop reference to my project and find the following article.
http://answers.mapr.com/questions/364/maven-repository-for-mapr-jar-files
I just don't understand how this happens.
Java can build with one jar but run with a different jar?
Thanks

yes. this is often the case with APIs (you compile the API, but at runtime you may run with a newer version of the API which may be included with the implementation). everything will work out fine as long as the classes/method prototypes referenced in your compiled code are unchanged from the jar you compiled against.
For a specific definition of compatibility, see binary compatibility (thanks to #MiserableVariable for the link).

Related

What is the difference between using javac and javax.tools.JavaCompiler?

Maven Compiler Plugin documentation states:
The Compiler Plugin is used to compile the sources of your project. Since 3.0, the default compiler is javax.tools.JavaCompiler (if you are using java 1.6) and is used to compile Java sources. If you want to force the plugin using javac, you must configure the plugin option forceJavacCompilerUse
And indeed when forceJavacCompilerUse is not specified in our build there are some build errors, for example when the code references the com.sun. packages (legacy, we know that its a bad idea...)
What are other differences between these two compile modes in general and with maven? Are there any output differences that one should know?
javac (as "java compiler") is an executable, which could be theoretically even a platform-dependent executable or a script. This is called to compile a .java to a .class.
On windows is its name javac.exe, and it is normally somewhere below C:\Program Files*\jdk*\bin.
This compiler was developed in java as well. That means, if we start this .exe, a new java virtual machine need to be started to run it. This is slow.
But, because it was written in Java, there is a much faster alternative to it: from our already running jvm, we simply import its main class (f.e. javax.tools.JavaCompiler or such) and call this. This doesn't need to start an unneeded jvm. That is what maven does. Simply 10 yrs was them enough to make this correctly. :-)
Of course it has some fallbacks as well. To most probable cause is that in the case of the internal compiler it needs to run from the same jvm and in the same namespace as the maven core. Also specifying an alternate jvm is impossible, and there could be some side effects as well resulting from the namespace collisions. But they are very improbable, because both of them is well-designed software.

Repackaging the .jar file

I need to add some jars from JRE7 library to my Android project. But for example rt.jar is in conflict with android.jar from Adroid 2.2 SDK, so I get this error:
Ill-advised or mistaken usage of a core class (java.* or javax.*)
when not building a core library.
This is often due to inadvertently including a core library file
in your application's project, when using an IDE (such as
Eclipse). If you are sure you're not intentionally defining a
core class, then this is the most likely explanation of what's
going on.
However, you might actually be trying to define a class in a core
namespace, the source of which you may have taken, for example,
from a non-Android virtual machine project. This will most
assuredly not work. At a minimum, it jeopardizes the
compatibility of your app with future versions of the platform.
It is also often of questionable legality.
If you really intend to build a core library -- which is only
appropriate as part of creating a full virtual machine
distribution, as opposed to compiling an application -- then use
the "--core-library" option to suppress this error message.
If you go ahead and use "--core-library" but are in fact
building an application, then be forewarned that your application
will still fail to build or run, at some point. Please be
prepared for angry customers who find, for example, that your
application ceases to function once they upgrade their operating
system. You will be to blame for this problem.
If you are legitimately using some code that happens to be in a
core package, then the easiest safe alternative you have is to
repackage that code. That is, move the classes in question into
your own package namespace. This means that they will never be in
conflict with core system classes. JarJar is a tool that may help
you in this endeavor. If you find that you cannot do this, then
that is an indication that the path you are on will ultimately
lead to pain, suffering, grief, and lamentation.
I know there have been several threads about it and things like JarJar, OneJar or FatJar might be good for me. But I don't know how to make any of them work and documentation doesn't really make it clear (for me). I guess they use Ant commands, but I have always used Eclipse built-in builder and now I have no idea how to use neither Ant nor any of mentioned above.
So my question is: how can I repack this rt.jar so I could compile it in my Android project?
Thank you!
EDIT:
Ok, so what I want to achieve is to create a .jar, which can be used during developing Android application (simplifies some functionalities, doesn't really matter). But I would also like to be able to add the very same .jar to standard Java project in order to use some functions there as well. It would look like this:
Whoever writes an application adds this .jar to his Java project -> it enables him to generate certain files (internet is needed to do it) -> these generated files are then added to Android project -> later on, when somebody uses this Android app, these files provide certain functionalities without using internet (off-line).
It would be ill-advised to do this in any project at all, even if it were possible. You would be opening yourself to a wealth of class incompatibility and loading problems. But in any case it doesn't even matter because the core Java libraries are loaded way before your archives are even touched, making any such attempt at overriding them moot.
Not to even talk about the fact that Android is using its own JVM implementation which is not fully compatible with JDK 6 (forget JDK 7). Also note that it may be a copyright violation to package the core Java libraries with your code and could change your licensing options (IANAL).
You need to find another way to resolve whatever issue you are having (which you failed to mention in your question).
There are many JARs that work nicely on both Android and on classic Java. None involve having Android developers pirate rt.jar. Stick to java.* and javax.* classes that exist in both the Android SDK and in whatever level of Java you are supporting, and your JAR will work fine in both environments.
You should ideally refrain from using such .jar files, but if you must, you can add them to build path. But this, at times results in a conflict, like the one that you are facing right now. What you need to do to resolve this kind of a conflict, is:
add the jar in the build path.
Check "referenced libraries". The jar file should appear under the same.
once it features under referenced libraries, check the "android dependencies" virtual directory. If you get to see that you have an instance of the same jar file there as well, you should delete the "android dependencies" folder altogether. (Trust me, this does not affect your project in any way).
having done that, you should be able to compile your code without any further conflicts.
Happy coding.. :)

Java compilation with two versions of Eclipse

I've got an old project in Eclipse 2.1 and compiled with a JDK 1.4.2_12. I want to upgrade the version of Eclipse to Eclipse Galileo.
I've imported my project and set the compilation level to 1.4 and I've also updated my build path to use the correct JDK.
The problem is that when I compare the compiled files in the classes folder in the two versions of Eclipse, the MD5 checksum are different.
Should I be worried about that fact or this is normal?
This is normal. The Eclipse compiler is free to change the class file structure as long as it conforms to the 1.4 JVM specification. If a 1.4 JVM will load and run the class, I see no issue.
For truly pedantic cases, you should do your final builds with a JDK which matches the JRE, and not the Eclipse compiler.
As long as the program is working i dont think that you need to worry!!!
As MD5 would be unique for each entity, and it represents the Hash value.
It's normal, but I would make sure about doing a complete rebuild anyway (possibly erasing any existing class files). My experience with Eclipse is that the incremental builder doesn't react well to unexpected situations.

Java Classloader - how to reference different versions of a jar

This is a common problem. I'm using 2 libraries A.jar and B.jar and these depend on different versions of the same jar.
Let's say that at runtime I need THIS.x.x.x.jar
MY.jar
-> A.jar -> THIS.1.0.0.jar
-> B.jar -> C.jar -> THIS.5.0.0.jar
I can compile the specific jar (A.jar/B.jar) against its dependency but at runtime I've to load only 1 version. Which one?
Loading only 1 dependency (the latest version) means that my code will probably throw runtime exceptions if the libraries are not Backward Compatible (are there Backward Compatible libraries out there?).
Anyway I know that something like OSGi can fix this issue.
I'm wondering what's the old way to fix this kind of problems...
Thanks a lot
"Old way" you mentioned (and the one OSGI certainly uses under the hood) is to install your own ClassLoader for both branches of your dependencies. That's how, for instance, application servers are able to run both older and newer versions of the same application inside the same JVM.
Read about classloader hierarchy.
In your setup, the tricky part is the joint point, where classes from both branches meet. Neither branches can use classes loaded into another one. The way to make it work is to make sure only classes loaded by boot classloader (JRE classes) or classloader of MY.jar are passed down to both branches.
OSGi can fix this problem. An OSGi bundle is nothing more than a jar with additional metadata detailing versions. A bundle has a version number, and will detail version numbers (or ranges) of dependent jars.
Take a look at this introductory Javaworld article for more information.
To solve this without OSGi means having to ensure manually that you compile and run with compatible jars. As you've discovered that's not necessarily a trivial task. Since jars don't necessarily identify their versions, the only sure way to do this to record/compare checksums or signatures.
Many libraries are backward compatible. But not all..
The old way is to try to depend from only one version.
It is probably safer to compile both with the same version (latest).
At least you get compile-time errors, instead of runtime errors.
If needed, you can modify a little bit your library that works with the old dependency...
This would require access to the source...
Please note that compile-time compatibility will not guarantee correct runtime behavior either. It is one step, then you can:
read the WhatsNew file for the new version of the jar
look on the Internet for users reporting compatibility problems
write JUnits
compare the codes in both jars
As mentioned by KLE, the default approach is to depend on the newer version. There is no guarantee, but most of the time this works. Probably the best way (while being a bloated one) is using OSGI to get over it.
To refer a basic "oldway" implementation checkout https://github.com/atulsm/ElasticsearchClassLoader
This provides an approach to handle non-backward compatible versions of elasticsearch client usage.

Java : Is there a tool to make code (in a 3rd party JAR) forward compatible (1.4 - 1.6)

I have a 3rd party JAR file that is compiled using Java 1.4. Is there a tool that can make the jar file compatible with Java 1.6? (Something like 'retrotranslator' but what does the reverse of it).
I tried decompiling the class files and re compile them in 1.6 but it fails.
Here is the issue:
My project uses 'rsadapter.jar' for was 5.1 and I had my project setup in Eclipse 2.0 + JDK 1.4 and it used to work fine. Now, I have migrated to Java 1.6 and Eclipse Ganymede (as per the requirements) and the same project (exactly same setup) started complaining about the missing class files in the 'rsadapter.jar'. I put the JAR in classpath explicitly too but it still could not load the classes. Then I changed the Java Compiler version to 1.4 and it started working.
Regards,
- Ashish
Classes compiled by JDK 1.4 should be usable in a Java 6 runtime as-is. If you have actually encountered a problem, please describe it.
Update: I can only reproduce this with types in the "default" package (that is, not in a package). Are the classes you are trying to use in the default package? Also, this happens to me regardless of the JDK version used to compile.
Update: Okay, after a little research, I realized that you can never reference a type in the unnamed package from a named package. Makes sense, but definitely not what you are running into.
I can compile code under JDK 1.4.2_19 and utilize it just fine in a Java 6 Eclipse project. I think that this problem is something specific to your environment. In this situation, I would backup Eclipse and recreate everything (JDK installation, workspace, projects) from scratch, to see if I could clear it up.
I had another issue with some legacy code written in Java 1.4.x: the authors loved enumerations and loved to name the corresponding variables 'enum'. They even used it for package names. And this prevents from compiling the code under Java 1.5 (or higher) quite successfully.
Changing that automatically is quite an issue.
May be you have defined Eclipse to throw compiler errors on use of deprecated methods or classes?

Categories

Resources