Accessing com.sun.tools.javac.util from Java 9 - java

I'm trying to access the List class from com.sun.tools.javac.util. This works fine with Java 8, but when switching to Java 9 I get the following error: Package 'com.sun.tools.javac.util' is declared in module "jdk.compiler", which does not export it to the unnamed module".
I tried adding requires jdk.compiler; to my module-info file, but this did not solve the issue.

In the longer run, the safe way to be dealing with such situation is to move away from using these internal APIs of the JDK.
One can make use of the jdk.compiler module's APIs as a replacement to the com.sun.tools.javac package.
Defines the implementation of the system Java compiler and its command
line equivalent, javac, as well as javah.
Specifically for com.sun.tools.javac.util.List, almost all of its non-overridden, self-defined methods could be derived from the implementation based on the interface java.util.List.
Migration guide's column about Removed java.* APIs state that -
The Java team is committed to backward compatibility. If an
application runs in JDK 8, then it will run on JDK 9 as long as it
uses APIs that are supported and intended for external use.
These include:
JCP standard, java.*, javax.*
JDK-specific APIs, some com.sun.*, some jdk.*
Supported APIs can be removed from the JDK, but only with notice. Find out if your code is using deprecated APIs by running
the static analysis tool jdeprscan.
Then to add to the highlighted risk above..
Compile Time
Internal APIs that are encapsulated in JDK 9 are not accessible at compile time, but can be made accessible at compile time via the --add-exports command-line option.
In your case :
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Run Time
At the runtime, they remain accessible if they were in JDK 8 but in a future release, they will become inaccessible, at which point the --add-exports or --add-opens options can be used to make them accessible at runtime as well.

gradle.settings with below arg it might help
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
kotlin.code.style=official

Related

Need Java Annotation to enforce javac -source option at compile time

We have some general utility classes in our large Java codebase that have to be deployed as a library in an environment where Java 1.6 is the only option. When this library is built, on infrequent occasions, we compile them with a Java 8 compiler with the -source option set to 1.6. The problem is, we've had to deal with compile errors that occur because someone has added code to the classes that use 1.7 or later Java language features. This happens because during the course of normal development, this code is developed and deployed in a Java 8 environment.
What I'm wishing for is a built-in annotation that will cause compilation to fail if it contains any code that is not 1.6-compatible, regardless of the compiler version in use or the runtime version it's targeting. Unfortunately, that doesn't seem to exist. Would it be possible to create a custom annotation for this? None of the literature I've found on custom annotations talks about creating custom annotations that do their thing at compile time.

Dependencies of a JAR

I created a Jar with Java 6. Now I'm creating a release document and recording the dependencies. How can I find the earliest version of Java that can successfully run the Jar, and the earliest version of Java that can successfully compile the source into a Jar?
I only know a manuel solution: try it out. There are, however, two things to consider.
For which version is the code language compatible?
For which JRE will it execute?
The first you can do with your current JDK, just iterate over the -source and -target arguments which you pass to your javac compiler. This will, however, not prevent you from using classes and methods from the JDK you are using. If you do, the code will not execute for a lower JRE, if you are using classes or methods that where not present back then.
The savest way would be to install all different JDKs along and try to compile the code with each of their compilers.
If you created the jar with java 6 and did not specify a different version of output bytecode, the generated class files will require Java 6 or greater. You can experiment to see what versions of bytecode you can generate with your source with the -target command line option if you're compiling manually, if you're using eclipse or some other IDE, most have settings that control the generated bytecode version in project options or somewhere similar.
A related post about determining the bytecode versions of class files: What version of javac built my jar?

Will compiling for Java 1.5 on Java 1.7 still work?

I've recently moved to Java 7 in one of my projects. I claim that it can run on Java 1.5 simply because there's nothing I depend on that is in Java 6 or 7. However when compiling today I noticed this:
bootstrap class path not set in conjunction with -source 1.5
Google has found little information on this warning. Does this mean that you can't compile to Java 1.5 from Java 1.7?
This Oracle blog explains the warning:
http://blogs.oracle.com/darcy/entry/bootclasspath_older_source
The reason is, that if you fail to set rt.jar for the older platform, then:
If the second step is not taken, javac will dutifully use the old
language rules combined with new libraries, which can result in class
files that do not work on the older platform since references to
non-existent methods can get included.
Does this mean that you can't compile to Java 1.5 from Java 1.7?
No it doesn't. It means that there is a right way and a wrong way to do this ... and you are doing it the wrong way.
The right way to compile for the Java 1.5 on a Java 1.7 JDK is:
Get hold of a copy of the "rt.jar" from Java 1.5 and put it on the compilation bootclasspath.
Compile with -source 1.5 and -target 1.5.
The warning message is telling you that you haven't done the first of these.
The way that you are building right now is implicitly using the 1.7 version of "rt.jar" for the Java runtime APIs. This may work! (Indeed, it should work assuming that you've made no changes to the code since it last built on 1.5.) However, there is a risk that you may accidentally introduce dependencies on classes or methods added in Java 1.6 or 1.7. That would result in runtime errors when you try to run your application on Java 1.5.
You better be setting -source and -target 1.5.
To be really sure that you aren't accidentally incorporating dependencies on newer classes, methods, or fields, use the maven-animal-sniffer plugin or something like it.
--source 1.5 will make sure the source files comply with Java 5 conventions. --target 1.5 will make sure the generated class files comply with Java 5 conventions. Neither of these will protect you from using Java 6 or 7 library methods. You must either compile against the appropriate rt.jar using --bootclasspath, or use something like the animal-sniffer-plugin (if you are using maven) which will inspect everything's type signature, and compare with published profiles.
With the animal-sniffer-plugin, you may be in for a treat, because you can bump into 3rd party libraries that use Java 6 APIs, which may cause your build process to fail given you are pursing Java 5.

How does javac locate the Java API classes?

The information on this page provides some good hints and general information, but nothing conclusive.
I know that javac.exe basically just launches 'sun.tools.javac.Main' from 'tools.jar' using Java. I also know that the Java API classes are stored in 'rt.jar' in 'jre/lib' under the JDK. Is this where 'javac.exe' loads the Java API classes from?
I'm wondering about this scenario in particular: suppose I installed jdk1.6.0_17, and later on I installed jdk1.6.0_25. If I run 'javac.exe' from jdk1.6.0_17, will it load the Java API classes from 'rt.jar' in 1.6.0_17 or 1.6.0_25?
The reason I want to know all of this is I'm in a situation where I know the code I'm compiling is going to run on a particular (not most recent) version of Java, and I want to compile using the same exact version of javac and the Java API to ensure compatibility.
By default, classes are compiled against the bootstrap(the runtime classes in rt.jar, internationalization classes in i18n.jar, and others) and extension classes of the platform that javac shipped with.So Yes If You run 'javac.exe' from jdk1.6.0_17, will it load the Java API classes from 'rt.jar' in 1.6.0_17 not from 1.6.0_25.
But javac also supports cross-compiling, where classes are compiled against a bootstrap and extension classes of a different Java platform implementation.
See more at : Cross Compilation if you want to use
Set the JAVA_HOME environment variable to the desired JDK. Also change your PATH environment variable to contain the desired JDK bin directory.

Java and "frontwards" compatiblity question

I've compiled my source with java version 1.6 using the parameters -source 1.5 and -target 1.5, and the compiler doesnt complain at all.
Still, the application won't run with java 1.5 due to missing methods. Ofcourse I could rewrite some of my source code to be 1.5 compliant, but what I don't understand is; shouldn't the java bytecode in the bottom be "frontwards" compliant?
Aren't the methods converted into bytecode? Is it possible to compile the 1.6 libs/methods (formely String.isEmpty()) to 1.5 bytecode and pack it all into the archive?
If you mean base Java library methods, then no, those methods are not converted to byte code when you compile; they've already been compiled to byte-code by Sun (or the third-party JVM distributer) and installed on your operating system. They are referenced and used by your compiled code.
The full set of command line options you need are:
java -source 1.5 -target 1.5 -bootclasspath /usr/jdk/jdk1.5.0_17/jre/lib/rt.jar
(Change bootclasspath to however your machine is setup.)
Of course, APIs enhancements in 1.6 will not be in 1.5. 1.5 is most of its way through its End of Service Life period, so you might want to consider a 1.6 minimum anyway.
I don't believe java will recompile the native java code backwards. So, if you make a 1.6 call - you will not be able to access it in 1.5
You can change the library you are compiling against to be an older library. In packages like eclipse, each installed JDK should appear in a "Select library" window, you can choose which one you wish to compile against.
If not, you should be able to override it in your ant file or CLI compile command.
If targeting an older JVM, this really has to be done or you may use calls that will not be available.
The source parameter only makes the compiler check at a language syntax level (source=1.4 would for example complain if it encounters generics) but won't restrict you to only using APIs available in the specified Java version.
The target parameter will make the compiler output class files that can be used by a runtime of the specified version but won't (just like -source) validate any API conformity.

Categories