I can see bcprov-jdk15 as well as bcprov-jdk16 on my project path. Can there be a scenario where we need both ?
The 15 and 16 point to JRE 1.5 and 1.6 for compatibility. Your version is 1.46 at most because that's the latest version where the JDK 1.5 and 1.6 were targeted separately. The 1.46 version was created on February 2011. The current version is 1.60, July 2018.
So you do not need nor should want either of those jars. You probably want the latest, otherwise you may be behind with regard to security fixes. Note that you should do some testing to see if the latest version runs with your code and change your code if it doesn't. Generally Bouncy Castle libs are backwards compatible, but some components such as its own ASN.1 API have gone through some serious changes.
So you'd better use this one from the Maven repository or download the latest from the Bouncy Castle site itself. You should use the one with 15on, which is for all versions of Java equal or greater than 1.5 (on = onwards).
Storing these jars without their version number is of course ludicrous. If you need to rename .jar files just to make your code run then there are some issues that you need to address.
The java version is relevant to Bouncy Castle. What you have are jars for Java 1.5 and Java 1.6
You should have only 1 in your classpath and use the Bouncy Castle jar closest to your Java runtime environment version. When you have more than one, you dont know which version of the code is being run. Class loading orders are not guaranteed and typically differs across environments, java versions, etc.
You are more likely to have bugs that are difficult to reproduce when you have two versions of the same jar.
What is important is the last 3 digits in the version e.g. 149 in bcprov-jdk15on-149.jar. This is the actual version of Bouncy Castle. Pick whichever is the newer one.
You should analyze your classpath dependencies (e.g. mvn dependency:tree) to understand which versions are you actually using. In principle the newer version should be backward compatible but this is not guranteed and there could be bugs.
Related
I am new to Java and am a bit confused about how this is working/how I should be working. I am using intellij and a project that I am working on its pom.xml has:
<java.version>11</java.version>
<maven.compiler.source>$(java.version)</maven.compiler.source>
<maven.compiler.target>$(java.version)</maven.compiler.target>
When I go into the project structure on intellij the module is using language level 11.
on my computer I just downloaded the newest JDK (17)?
Does this cause issues working like this? Should I only be using a JDK that is associated with the version I am working?
I have not had any issues... but I am afraid my dependencies might be different than the ones I should be using...or the the build will be different if someone else is using another jdk.
The JDK version specified in the pom.xml specifies what source and target version is passed to javac. This specifies what system libraries and language features can be used.
The language level from IntelliJ matches this.
The installed JDK is the program (or set of programs) used for compiling and running the application.
Java allows to use a newer JDK to compile programs for older (source/target/release) versions.
The produced class files (bytecode) should be the same no matter what JDK version you use as long as the target/release version (specified in the pom.xml/language level in IntelliJ) is the same.
Furthermore, Java is (almost completely) backwards compatible. When writing code for an old Java version, it will likely also work in newer Java versions.
I regularly use MALLET for topic modeling in the classes that I teach. Running MALLET requires users to have the Java Development Kit installed. I currently have JDK 8 update 241 installed on my main computer, and I know that MALLET works properly in this environment. That said, JDK is now up to v14.
Which version(s) of JDK does MALLET support?
I'm not altogether sure that you do need the JDK. They never say that on the website. The tarfile that I downloaded already includes compiled classes - you aren't expected to build it from source - so the JRE should be enough.
Strangely enough, the compiled classes in the class directory are targeted at 1.7 (bytecode version 51) whereas the pom indicates that it's supposed to target Java 1.6. So it's quite probable that by rebuilding it you could support an older version of Java.
In any case, the JDK is backwards compatible by design. Any version from 7 onwards will be able to run it (6+ if you were to rebuild it).
Running it on a newer version will benefit from the new features of the JDK, such as improvements to the garbage collector, so you may see some performance improvement there. If you are not concerned about that then it doesn't matter.
Recently, a teammate used the following function in our Java 8 code: Matcher.replaceAll​(Function replacer).
The function was introduced in Java 9, but because he is using a newer compiler, the API function was simply found in the JDK's rt.jar and nobody noticed this won't work under real Java 8 environments.
The compatibility settings are correctly set, and the gradle subproject has the following settings:
sourceCompatibility = 1.8
targetCompatibility = 1.8
I had very similar issues at the time when I first used the Java 6 function String.isEmpty in Java 5 code - the code made it into the release and crashed there.
What can I do to enforce the usage of the correct API. As it is a shared library, do I have to use (and install, maintain..) a different JDK for this gradle subproject, or is there some kind of compatibility scanner which runs through a built jar and checks all rt references?
As you've noticed, the two compatibility configurations does not consider the APIs of older versions - only the syntax, semantics and the resulting byte code.
There are two options you can take. One is to have JDK 8 installed on your computer, and the configure Gradle to use it when compiling your project. It looks like this:
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.executable = "$java8Home/bin/javac"
options.bootstrapClasspath = files("$java8Home/jre/lib/rt.jar")
}
The disadvantage here is that you will need to have JDK 8 installed in the first place, and as it will probably be installed in different locations, you will need probably want to configure it with an environment variable or property (I've called it java8Home here).
However, since Java 9, the JDK now knows about the documented APIs of previous versions, and you can select which one to use with a new --release flag. This is not going to work if you use undocumented APIs, but it means you can compile your project with any versions of Java and still make the resulting classes compatible with Java 8. You can do it like this:
tasks.withType(JavaCompile) {
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
options.compilerArgs.addAll(['--release', '8'])
}
}
Note that the 'if' statement is only there in case you still need to support running Gradle with Java 8 (through your JAVA_HOME variable). If you are only using later versions, it can be removed so you always set the 'compilerArgs'.
For some versions of Java, it is possible build Java code on a newer JDK to run on an older JDK / JRE. You have already discovered the --source and --target options for javac and the corresponding Gradle settings. The other thing you can do is to use --bootclasspath to tell javac to compile against the runtime libraries for an older version of Java.
Since you are using Gradle, check out "gradle-java-cross-compile-plugin" (https://github.com/nebula-plugins/gradle-java-cross-compile-plugin). I can't find any documentation for it, but it apparently deals with --target and --bootclasspath.
Having said that, I don't think cross-compiling Java is a good solution.
I would actually recommend that you set up a Continuous Integration (CI) server (e.g. Jenkins) with JDK installations for all of the Java versions you are interested in supporting. Then set up jobs to build your code and run your unit tests for each Java versions.
Note that simply compiling your code against the older Java libraries is not sufficient to verify backwards compatibility. Sometimes the behavior of libraries changes. You need to run your tests, and your tests need to cover the cases where compatibility issues may exist.
We're using java 8 for most modules/projects, but for some of the modules, we use java 6 (customer requirements).
The developers have java 8 installed and we compile the java 6 projects using these flags:
compileJava {
sourceCompatibility = 1.6
targetCompatibility = 1.6
}
We thought we're all good until we upgraded guava from v20 to latest - 28.1-jre.
To our surprise, the build was successful but failed at runtime.
We have a workaround for building for java 6 using a specific javac found in JDK 6. See more info here. This workaround wields the error class file has wrong version 52.0, should be 50.0 in compile time. The downside is that it requires a download+config+usage of JDK 6 for developers.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java) Thanks.
Setting -source and -target values to 1.6 is insufficient to ensure that the resulting output is compatible with 1.6. The program itself must not have any library API dependencies on later versions, and the -source and -target options don't do that. (GhostCat said pretty much the same thing.)
For example, in Java 8, ConcurrentHashMap added a covariant override for the keySet method that returns a new type ConcurrentHashMap.KeySetView. This type didn't exist in earlier versions of Java. However, in the class binary, the return type is encoded at the call site. Thus, even if the source code is compiled with -source 1.6 -target 1.6, the resulting class file contains a dependency on the Java 8 class library API.
The only solution to this is to ensure that only Java 1.6 compatible libraries are in the classpath at compile time. This can be done using the -Xbootclasspath option to point to a JDK 1.6 class library, or it might be simpler just to use a JDK 1.6 installation in the first place.
This applies to external libraries in addition to the JDK, as you've discovered with Guava. The Animal Sniffer project provides plugins for Ant and Maven that checks library dependencies for version problems. Offhand I don't know if there is something similar for Gradle. There might be a way to get Animal Sniffer to work with Gradle, but I have no experience with doing that.
Is there a way to validate the dependencies' java version at compile time when using a higher java version? (without installing lower version java).
You specify your dependencies. When you tell your built system to explicitly use some library X in version Y, then you made a very clear statement.
And you see, it is not only about the class file version number. What if some person doesn't pay attention, and compiles something with Java8 ... with Java6 target, but forgets that the code bases uses Java8-only API calls?!
In other words: you are looking in the wrong place.
The person who makes updates to the build description, and changes a library version from Y to Y+8, that person needs to carefully assess that change. For example by reading release letters.
I agree that a really clever build system could check if libraries you are using come in with a matching class file version. But as said, that is only one aspect of the problem. So instead of looking into a technical solution, I think the real answer is: don't step version numbers because you can, but because you have to. And that manual step of changing that version number, that is something that requires due diligence (on the side of the human doing it).
Thus: I think the most sane approach here is to compile the Java6 deliverables within their own specific build setup. Which you only touch after careful inspection of such details. And sure: convince your customer to move on, and give up a long dead version of Java.
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.