Bug 56134 - java task with fork=true uses different JVM than the one set in JAVACMD environment variable
Summary: java task with fork=true uses different JVM than the one set in JAVACMD envir...
Status: NEW
Alias: None
Product: Ant
Classification: Unclassified
Component: Core tasks (show other bugs)
Version: 1.9.4
Hardware: PC Solaris
: P2 normal (vote)
Target Milestone: ---
Assignee: Ant Notifications List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-02-13 10:38 UTC by A. Knauf
Modified: 2014-05-06 03:20 UTC (History)
0 users



Attachments
Proof of concept project for demonstration of the issue (1.17 KB, application/zip)
2014-02-13 10:38 UTC, A. Knauf
Details

Note You need to log in before you can comment on or make changes to this bug.
Description A. Knauf 2014-02-13 10:38:30 UTC
Created attachment 31308 [details]
Proof of concept project for demonstration of the issue

Scenario
========

* Solaris 64bit OS with both a 32bit JVM and a 64bit JVM installed (Oracle) 
* Setting the JAVACMD environment variable as per the documentation[0] to specify the 64bit binary to be used
* Running a build which calls the java task with attribute fork="true"

Expected
========

Ant continues to use the java executable as specified in the environment variable.

Actual
======

Ant spawns a JVM taken from the OS/environment defaults, thus mixing the build process with 64bit and 32bit processes. This may result in build errors, esp. when using native libraries which require a certain architecture.

How to reproduce
================

Please find attached a simple proof-of-concept. The Java main class simply prints the architecture. In our case we can reproduce the behaviour like this:

--------------------------------------
-bash-3.2$ export JAVACMD=/opt/database/www/apps/java/bin/amd64/java

-bash-3.2$ $JAVACMD -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)

-bash-3.2$ grep classname build.xml
    <java fork="true" classname="Main">

-bash-3.2$ ant
Buildfile: /tmp/sup11111/build.xml

compile:

run:
     [java] JVM Arch: 32bit

BUILD SUCCESSFUL
Total time: 1 second
-bash-3.2$
--------------------------------------

When removing the fork="true" park from build.xml, the correct JVM is takes, as specified in the environment variable:

--------------------------------------
-bash-3.2$ grep classname build.xml
    <java classname="Main">

-bash-3.2$ ant
Buildfile: /tmp/sup11111/build.xml

compile:

run:
     [java] JVM Arch: 64bit

BUILD SUCCESSFUL
Total time: 0 seconds
--------------------------------------

I know that the java task allows the JVM to be specified explicitly using the "jvm" attribute and other settings via the "jvmargs" etc. However, this would not work when trying to reproducibly run the build on different environments with different paths.

[0] http://ant.apache.org/manual/running.html#envvars
Comment 1 A. Knauf 2014-02-13 14:36:00 UTC
I did some additional digging and found out that the Java task definition instantiates a CommandlineJava object (at the very top of the class), which in its constructor determines the java executable using the following piece of code:


    /**
     * Constructor uses the VM we are running on now.
     */
    public CommandlineJava() {
        setVm(JavaEnvUtils.getJreExecutable("java"));
        setVmversion(JavaEnvUtils.getJavaVersion());
    }

(CommandlineJava.java @ 225ff)

The referred-to JavaEnvUtils.getJreExecutable("java") finds the first matching Java executable within a given JAVA_HOME directory. Unfortunately for Solaris, the directory structure of the JDK/JRE is like this:

$JAVA_HOME/bin/              # 32bit executables: java, javac, ..
$JAVA_HOME/bin/amd64/        # 64bit executables: java, javac, ..

This means, regardless of environment specifications, the first matching Java executable will always be the 32bit one on this environment.

In addition, the environment variable $JAVACMD which I mentioned in my initial description is only used inside the (shell, batch) wrapper scripts, so this setting will also not be honored from inside JavaEnvUtils.

Does it make sense to extend the logic inside JavaEnvUtils to also take into account this variable when trying to determine which Java executable to use? 

Without having any further knwoledge of the code base, it seems to be as if this may be a simple extension of the getJreExecutable(..) method. However, I cannot oversee what other parts depend on it and may be affected by a change.