How to run two java versions in the same project? - java

I have created a project in Java Eclipse which uses java 1.7. But I need to run some specific modules in it using java 1.8.
Is it possible? How?

you can set the Java compiler level only project wise not the module wise.
So in your case you should set the compiler level 1.8 which will support both the version.
In eclipse you can set the compiler level by below option.
Right click on Project->Properties->Java Compiler.

Simply spoken: you shouldn't do that.
If you are really talking about one project; then you have to make sure that all "components" within your project are on compatible levels.
In your case: if component A requires Java 1.8; and others are fine with 1.7 ... then you should go forward and use 1.8 (you still can use libraries that were compiled for older versions of Java; no need to update/recompile them). And well, if one part needs 1.8; and another only works 1.7 ... then there is no easy solution to that.
The point is: if you deviate from this practice, you will have to use multiple JVMs later on to run your "single" project - and that is of course a contradiction in itself.
The alternative is to dissect the one project you have right now into smaller parts (nowadays you would call them microservices) and define an architecture that allows you to run different parts of your application using different technology. But as others have pointed out: that adds a whole new layer of complexity to your setup.

Related

Maven/Retrolambda: how to detect dependencies on Java 8 classes

Background:
We have maven-based java project, which targets JRE 1.7, but the source code uses lambdas, so we use retrolambda for transforming Java 8 source code to Java 7. Also we use StreamSupport backport library when we need streams, function.*, Optional, etc.
Usage of retrolambda involves configuring the project's both source and target language level to 1.8.
Everything works fine if there are no dependencies on java8 classes or methods (like java.util.stream.*, java.util.Optional, or methods introduced in java8 like Collection.forEach). If there are such usages then build passes, but it fails in runtime, when running under JVM of Java 8.
Question:
My goal is to fail the build in case when such dependencies exist. Is there any way of detecting dependencies on new Java 8 classes/methods in build-time?
I thought about 2 possible options, but I'm not sure whether either of them is doable:
Some kind of bytecode analyzer for detecting depdencies on predefined classes and methods. Are there such tools/maven plugins?
Lint (lint4j) rules. Not sure whether it's possible to detect dependency on class/method using lint
You can use the Animal Sniffer Maven Plugin for this. It allows you to check that your code only uses APIs from a specified baseline (called the "signature"). In your case you'd use the org.codehaus.mojo.signature:java17:1.0 signature.
As others pointed out, you also could set up the bootstrap classpath, but that a) requires a JDK 7 to be set up and b) makes the build a bit more complex as you need to point to the JDK 7 install. Animal Sniffer is is much easier to work with in my experience.

How to list the JDK compatibility (like "sourceCompatibility") of all of the dependencies with a gradle project?

I need to know the Java runtime compatibility of all of the dependencies I'm using in my gradle project (as if it were checking each project's "sourceCompatibility" setting so to speak), preferrably without perusing each dependency's documentation to find it, if it's there at all. Is there a way to do this with gradle? Or even some other automating tool?
(Specifically I'm trying to see which dependencies might be using Java 8 features like Streams, since I'm trying to compile for Android with retrolambda, and iOS with RoboVM.)
Such information probably does not exist, and is very tedious to generate.
"sourceCompatibility" does not exist after the source is compiled-- it's just to tell the compiler how to interpret the source syntax. What you're more likely to be interested in is the "targetCompatibility", or the class file format major version: Java 8 is 52, Java 7 is 51, etc.
This tells Java that Java 8 is required to understand the class format and bytecodes contained in the class, so you could download and unpack every dependency in your project, and all of their dependencies, and then look at the version number of every class file, except...
Simply looking at the class file format version doesn't tell you whether the class makes reference to methods and fields that exist only in specific versions of the JDK. I've not tested with Java 8, but in Java 7 it's possible to set the source/target compatibility to 1.6 and still reference new methods that were added in Java 7. A Java 6 JVM will load and run the file, but fail with NoSuchMethodException despite otherwise looking perfectly fine.
The only way to realistically check if a dependency is completely compatible with a different version of Java than the one it was compiled for is to go through the constant pool of every class, find every class and method reference, and then verify that they are valid for the desired JRE.
You will want an automated tool for this (The JVM could do it if you have the desired version of Java installed and 100% coverage in your unit tests...), but I don't know of a standalone tool that does, and neither gradle nor project documentation usually includes this sort of info.

Run Java packages on unsupported Java Version

I have a PowerMac and it is giving me bad version number on some .jars. I would love to make it seem like I am running Java 6. How would I spoof the version? Let me also say I am running PowerPC and Leopard
The most likely problem is that you have Java 6 JAR files and you are trying to run them on an old Java installation.
How would I spoof the version?
The answer to your question is that you can't. The way to run Java 6 specific JAR files it to use a Java 6 (or later) JRE or JDK.
The problem is that the format of Java class files has changed, and your installation can't cope with the new format. And this is not a gratuitous change that you can pretend doesn't exist. Java 6 (actually Java 5) has support for generic types, enums, annotations and other things. Assuming that the JARs contain code that uses these new language features, an older JRE simply won't know what to do with them.
There are two solutions:
Upgrade your Java installations to the required level on all machines. This is the best solution ... if it is an option ... because it means your users will get the benefit of security and bug fixes and performance enhancements. (And progress of your project won't be held back by the constraint of supporting legacy platforms.)
Compile all of your code for compatibility with the oldest version of Java that you still have to use. Either compile on the corresponding old JDK, or on a more recent JDK using appropriate -source / -target / -Xbootclasspath options ... as described by the javac manual page.
The catch with the second solution is that if the source code for the JAR files in question uses recently added Java language features or APIs, then recompiling for the older platform will fail. To fix this you will need to rewrite your code to replace the nice modern stuff with archaic stuff. Not a good solution, IMO.
The other possibility is that you are seeing corrupted JAR files. This is unlikely, but it can happen if you are using applets or webstart, and the server is delivering error pages instead of JAR files.
The third possibility is that you simply haven't configured your Mac's Java installation's correctly. Making Java 7 the default should allow you to run everything without class version problems. (Thanks #paulsm4) Note that I can't help you with that ... 'cos I don't use Java on a Mac.

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