Maven - Use JDK 7 to Compile for JVM 5

18,161

Solution 1

This answer is targeted at the title of the question, not to the specific problems of the question in detail.

I ended up using this solution in my project, which allows me to use the custom bootstrap classpath selectively, by activating a maven profile. I strongly recommend using a profile for this, because otherwise it makes the build fail for anyone that does not have the environment variable set (very bad, especially for an open source project). I only activate this profile in my IDE for the "Clean & Build" action.

    <profile>
        <id>compileWithJava5</id>
        <!--
            NOTE
            Make sure to set the environment variable JAVA5_HOME
            to your JDK 1.5 HOME when using this profile.
        -->
        <properties>
            <java.5.home>${env.JAVA5_HOME}</java.5.home>
            <java.5.libs>${java.5.home}/jre/lib</java.5.libs>
            <java.5.bootclasspath>${java.5.libs}/rt.jar${path.separator}${java.5.libs}/jce.jar</java.5.bootclasspath>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.5</source>
                        <target>1.5</target>
                        <compilerArguments>
                            <bootclasspath>${java.5.bootclasspath}</bootclasspath>
                        </compilerArguments>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>

Solution 2

Previous version of Java we not particularly good at supporting previous versions of Java. For Java 7 it appears to be much better.

Here is a program which should compile under any version.

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

$ javac -target 1.7 -source 1.7 Main.java
$ javac -target 1.6 -source 1.6 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.6
1 warning
$ javac -Xbootclasspath:/usr/java/jdk1.6.0_29/jre/lib/rt.jar -target 1.6 -source 1.6 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.5.0_22/jre/lib/rt.jar -target 1.5 -source 1.5 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.4.0_30/jre/lib/rt.jar -target 1.4 -source 1.4 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.3.1_29/jre/lib/rt.jar -target 1.3 -source 1.3 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.2.2_017/jre/lib/rt.jar -target 1.2 -source 1.2 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.1.8_16/jre/lib/rt.jar -target 1.1 -source 1.2 Main.java
$ javac -Xbootclasspath:/usr/java/jdk1.1.8_16/jre/lib/rt.jar -target 1.1 -source 1.1 Main.java
javac: invalid source release: 1.1
Usage: javac  
use -help for a list of possible options
$ javac -Xbootclasspath:/usr/java/jdk1.1.8_16/jre/lib/rt.jar -target 1.0 -source 1.0 Main.java
javac: invalid target release: 1.0
Usage: javac  
use -help for a list of possible options

If you need to compile for a previous version of Java, you need to provide a bootclasspath, ideally for the version of Java you want to compile for. Java 7 appears to be able to support all the way back to Java 1.2

Solution 3

You can use the bootclasspath configuration option on the maven-compiler-plugin if needed:

<compilerArguments>
    <bootclasspath>xxxxxxxxx</bootclasspath>
</compilerArguments>

You can read more about it here. See the note under the example.

Solution 4

The problem is with Java 7 backwards compatibility.

There are very few classes that don't preserve backwards compatibility due to impossible to resolve issues of different nature, DataSource happens to be one of them.

So, either you adapt your class to respect the new signatures (even when in backwards compatibility mode), or you'll be forced to use a different version of the virtual machine.

You can read further information here: http://www.oracle.com/technetwork/java/javase/compatibility-417013.html

Solution 5

To use multiple Jars in the Maven compiler options, use the ${path.separator} string between the jars:

<compilerArguments>
   <bootclasspath>${env.JAVA5_HOME}/jre/lib/rt.jar${path.separator}${env.JAVA5_HOME}/jre/lib/jce.jar${path.separator}${env.JAVA5_HOME}/jre/lib/jsse.jar</bootclasspath>
</compilerArguments>
Share:
18,161
Alex Ciminian
Author by

Alex Ciminian

Updated on June 20, 2022

Comments

  • Alex Ciminian
    Alex Ciminian almost 2 years

    I've been trying to get this to work for a while now but no luck yet.

    I want to run with JAVA_HOME pointing to JDK7 but I want to compile a project for JVM 5. I've read through documentation, I've found similar posts on SO, but none of them seem to work in my setup.

    I first tried with setting just target and source but I got an error:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.5</source>
            <target>1.5</target>
        </configuration>
    </plugin>
    

    [ClassName] is not abstract and does not override abstract method getParentLogger() in CommonDataSource

    As far as I understood that class was updated in JDK 7 and the extra method that's throwing the error was just added. I need to use the runtime of JDK 5 that has the old implementation and everything should work fine. So I do this:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <verbose>true</verbose>
            <source>1.5</source>
            <target>1.5</target>
            <compilerArguments>
                <bootclasspath>${env.JAVA5_HOME}/jre/lib/rt.jar</bootclasspath>
            </compilerArguments>
        </configuration>
    </plugin>
    

    I have JAVA5_HOME set correctly on my system, I can see it loading the correct classes in the log, but I hit another error:

    [loading ZipFileIndexFileObject[c:\Program Files\Java\jdk1.5.0_22\jre\lib\rt.jar(*.class)]]
    ...
    ...
    [ClassName] error: package javax.crypto does not exist

    Which is fair enough, since I didn't include jce.jar (cryptography classes) in the bootclasspath. There is a thing that puzzles me, though. Even though the bootclasspath contains only the Java 5 runtime, I have a lot of libraries from JRE7 in the classpath. They are not specified anywhere.

    [search path for class files: c:\Program Files (x86)\Java\jdk1.5.0_22\jre\lib\rt.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\dnsns.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\localedata.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\sunec.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\sunjce_provider.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\sunmscapi.jar,c:\Program Files\Java\jdk1.7.0_02\jre\lib\ext\zipfs.jar, ...]

    If I try and add jce.jar (from JRE5), I get back to the first error:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <verbose>true</verbose>
            <source>1.5</source>
            <target>1.5</target>
            <compilerArguments>
                <bootclasspath>${env.JAVA5_HOME}/jre/lib/rt.jar${path.separator}${env.JAVA5_HOME}/jre/lib/jce.jar</bootclasspath>
            </compilerArguments>
        </configuration>
    </plugin>
    

    The type [ClassName] must implement the inherited abstract method CommonDataSource.getParentLogger()

    I also see no trace of rt.jar being loaded, but I don't get a java.lang not found error, so there are some classes being loaded on the classpath.

    I'll fix it temporarily by making a batch script that overwrites JAVA_HOME before building and sets it back afterwards, but I really want this done the right way. This doesn't seem as such an extreme use-case. :)

    What am I doing wrong here?

  • Alex Ciminian
    Alex Ciminian about 12 years
    Even if I use <source> and <target>? I understood from the docs that this is what they're supposed to do - produce code compatible with other JVMs.
  • user1568901
    user1568901 about 12 years
    You can compile code for earlier JVMs under the latest JDK. It will produce the appropriate byte code and throw errors if you try to use language constructs not available in the target JVM. You just need to use extra caution as javac won't prevent you from using new methods/classes that aren't in the old JVM standard system...
  • Vishy
    Vishy about 12 years
    @AlexCiminian You are using the correct option, the problem is whether your javac supports this option. Java 5.0 & 6 were very poor at compiling for previous versions, however Java 7 appears to cover back to Java 1.2 If you use an older version of the rt.jar it should prevent you using methods/functionality which doesn't exist in your target version.
  • Alex Ciminian
    Alex Ciminian about 12 years
    Check out my examples, I'm using it that way but it doesn't work if I specify multiple jars. Am I missing something?
  • Alex Ciminian
    Alex Ciminian about 12 years
    Well, it 'works' if I specify rt.jar, but it breaks because of other dependencies needed from the runtime (jce.jar). If I try to add both, it's as if I haven't specified rt.jar at all (my last example). I've tried doing this only in Maven, I'll try in the command line, maybe it's a maven bug.
  • Vishy
    Vishy about 12 years
    strange, I just tried basically what you have without the ${env... and it worked just fine. Can you try removing the environment variable?
  • Alex Ciminian
    Alex Ciminian about 12 years
    Nope, doesn't work. Tried it with the full path instead of the environment var, but it behaves the same in both cases (one jar, two jars).
  • Vishy
    Vishy about 12 years
    Can you clarify what you mean by "doesn't work". Can you try compiling with the -X option for maven to give you more debugging information?
  • Alex Ciminian
    Alex Ciminian about 12 years
    I get the two errors listed in my post, under each configuration. If I specify just rt.jar it breaks because it doesn't find javax.crypto. If I try and give it both, I get back to the unimplemented method error (it's probably using the JRE7 classes instead of JRE5). If I give it just jce.jar it breaks because it doesn't find java.lang - which is normal. I don't understand why it doesn't use the jre I'm feeding it if I try and specify multiple <bootclasspath> jars. Do I need to alter the classpath?
  • Vishy
    Vishy about 12 years
    I would try doing the same compilation on the command line to see if it gives you a more informative error message. Perhaps with a simpler example. (I was only compiling a hello World program)
  • Alex Ciminian
    Alex Ciminian over 10 years
    If you take a look at my example, this is what I was doing.