8

Does anyone know how to programatically get all JVMs installed (not the default one) using Java?

For example, there are 2 JVMs installed on a user's machine:

JDK 5
JDK 6

I need to know all the versions installed in order to switch the one that it is on use (by default) and then call javac programatically to compile some source code using a specific JDK version.

I've been looking for some info on the web, I found:

But I couldn't find what I was looking for.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Florencia
  • 81
  • 1
  • 1
  • 2

10 Answers10

17

I've recently dealed with a very similar situation. The following code does almost exactly what you need. It searches for java JREs and JDKs, not only JDKs, but should be pretty easy to edit to your needs. Beware: windows-only

/**
 * Java Finder by petrucio@stackoverflow(828681) is licensed under a Creative Commons Attribution 3.0 Unported License.
 * Needs WinRegistry.java. Get it at: https://stackoverflow.com/questions/62289/read-write-to-windows-registry-using-java
 *
 * JavaFinder - Windows-specific classes to search for all installed versions of java on this system
 * Author: petrucio@stackoverflow (828681)
 *****************************************************************************/

import java.util.*;
import java.io.*;

/**
 * Helper class to fetch the stdout and stderr outputs from started Runtime execs
 * Modified from http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
 *****************************************************************************/
class RuntimeStreamer extends Thread {
    InputStream is;
    String lines;

    RuntimeStreamer(InputStream is) {
        this.is = is;
        this.lines = "";
    }
    public String contents() {
        return this.lines;
    }

    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader    br  = new BufferedReader(isr);
            String line = null;
            while ( (line = br.readLine()) != null) {
                this.lines += line + "\n";
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();  
        }
    }

    /**
     * Execute a command and wait for it to finish
     * @return The resulting stdout and stderr outputs concatenated
     ****************************************************************************/
    public static String execute(String[] cmdArray) {
        try {
            Runtime runtime = Runtime.getRuntime();
            Process proc = runtime.exec(cmdArray);
            RuntimeStreamer outputStreamer = new RuntimeStreamer(proc.getInputStream());
            RuntimeStreamer errorStreamer  = new RuntimeStreamer(proc.getErrorStream());
            outputStreamer.start();
            errorStreamer.start();
            proc.waitFor();
            return outputStreamer.contents() + errorStreamer.contents();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return null;
    }
    public static String execute(String cmd) {
        String[] cmdArray = { cmd };
        return RuntimeStreamer.execute(cmdArray);
    }
}

/**
 * Helper struct to hold information about one installed java version
 ****************************************************************************/
class JavaInfo {
    public String  path;        //! Full path to java.exe executable file
    public String  version;     //! Version string. "Unkown" if the java process returned non-standard version string
    public boolean is64bits;    //! true for 64-bit javas, false for 32

    /**
     * Calls 'javaPath -version' and parses the results
     * @param javaPath: path to a java.exe executable
     ****************************************************************************/
    public JavaInfo(String javaPath) {
        String versionInfo = RuntimeStreamer.execute( new String[] { javaPath, "-version" } );
        String[] tokens = versionInfo.split("\"");

        if (tokens.length < 2) this.version = "Unkown";
        else this.version = tokens[1];
        this.is64bits = versionInfo.toUpperCase().contains("64-BIT");
        this.path     = javaPath;
    }

    /**
     * @return Human-readable contents of this JavaInfo instance
     ****************************************************************************/
    public String toString() {
        return this.path + ":\n  Version: " + this.version + "\n  Bitness: " + (this.is64bits ? "64-bits" : "32-bits");
    }
}

/**
 * Windows-specific java versions finder
 *****************************************************************************/
public class JavaFinder {

    /**
     * @return: A list of javaExec paths found under this registry key (rooted at HKEY_LOCAL_MACHINE)
     * @param wow64  0 for standard registry access (32-bits for 32-bit app, 64-bits for 64-bits app)
     *               or WinRegistry.KEY_WOW64_32KEY to force access to 32-bit registry view,
     *               or WinRegistry.KEY_WOW64_64KEY to force access to 64-bit registry view
     * @param previous: Insert all entries from this list at the beggining of the results
     *************************************************************************/
    private static List<String> searchRegistry(String key, int wow64, List<String> previous) {
        List<String> result = previous;
        try {
            List<String> entries = WinRegistry.readStringSubKeys(WinRegistry.HKEY_LOCAL_MACHINE, key, wow64);
            for (int i = 0; entries != null && i < entries.size(); i++) {
                String val = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE, key + "\\" + entries.get(i), "JavaHome", wow64);
                if (!result.contains(val + "\\bin\\java.exe")) {
                    result.add(val + "\\bin\\java.exe");
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return result;
    }

    /**
     * @return: A list of JavaInfo with informations about all javas installed on this machine
     * Searches and returns results in this order:
     *   HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment (32-bits view)
     *   HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment (64-bits view)
     *   HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit     (32-bits view)
     *   HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit     (64-bits view)
     *   WINDIR\system32
     *   WINDIR\SysWOW64
     ****************************************************************************/
    public static List<JavaInfo> findJavas() {
        List<String> javaExecs = new ArrayList<String>();

        javaExecs = JavaFinder.searchRegistry("SOFTWARE\\JavaSoft\\Java Runtime Environment", WinRegistry.KEY_WOW64_32KEY, javaExecs);
        javaExecs = JavaFinder.searchRegistry("SOFTWARE\\JavaSoft\\Java Runtime Environment", WinRegistry.KEY_WOW64_64KEY, javaExecs);
        javaExecs = JavaFinder.searchRegistry("SOFTWARE\\JavaSoft\\Java Development Kit",     WinRegistry.KEY_WOW64_32KEY, javaExecs);
        javaExecs = JavaFinder.searchRegistry("SOFTWARE\\JavaSoft\\Java Development Kit",     WinRegistry.KEY_WOW64_64KEY, javaExecs);

        javaExecs.add(System.getenv("WINDIR") + "\\system32\\java.exe");
        javaExecs.add(System.getenv("WINDIR") + "\\SysWOW64\\java.exe");

        List<JavaInfo> result = new ArrayList<JavaInfo>();
        for (String javaPath: javaExecs) {
            if (!(new File(javaPath).exists())) continue;
            result.add(new JavaInfo(javaPath));
        }
        return result;
    }

    /**
     * @return: The path to a java.exe that has the same bitness as the OS
     * (or null if no matching java is found)
     ****************************************************************************/
    public static String getOSBitnessJava() {
        String arch      = System.getenv("PROCESSOR_ARCHITECTURE");
        String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
        boolean isOS64 = arch.endsWith("64") || (wow64Arch != null && wow64Arch.endsWith("64"));

        List<JavaInfo> javas = JavaFinder.findJavas();
        for (int i = 0; i < javas.size(); i++) {
            if (javas.get(i).is64bits == isOS64) return javas.get(i).path;
        }
        return null;
    }

    /**
     * Standalone testing - lists all Javas in the system
     ****************************************************************************/
    public static void main(String [] args) {
        List<JavaInfo> javas = JavaFinder.findJavas();
        for (int i = 0; i < javas.size(); i++) {
            System.out.println("\n" + javas.get(i));
        }
    }
}

You will also need the updated WinRegistry.java to read values from both the from 32-bits and 64-bits sections of the windows registry: https://stackoverflow.com/a/11854901/828681

I'm not usually a java programmer, so my code probably does not follow java conventions. Sue me.

Here is a sample run from my Win 7 64-bits machine:

>java JavaFinder

C:\Program Files (x86)\Java\jre6\bin\java.exe:
  Version: 1.6.0_31
  Bitness: 32-bits

C:\Program Files\Java\jre6\bin\java.exe:
  Version: 1.6.0_31
  Bitness: 64-bits

D:\Dev\Java\jdk1.6.0_31\bin\java.exe:
  Version: 1.6.0_31
  Bitness: 64-bits

C:\Windows\system32\java.exe:
  Version: 1.6.0_31
  Bitness: 64-bits

C:\Windows\SysWOW64\java.exe:
  Version: 1.6.0_31
  Bitness: 32-bits
Community
  • 1
  • 1
Petrucio
  • 5,491
  • 1
  • 34
  • 33
  • 1
    I just noticed that running this program with a 32-bit java.exe sends me to a different section of the registry, and shows me C:\Program Files (x86)\Java\jre6\bin\java.exe instead. I'll bebug and update my post when this is fixed – Petrucio Aug 07 '12 at 19:31
  • you should not use back slashes in Java pat hstrings. Always use slash. Under the hood the file system implementation will convert the directory separator automatically. – Weltraumschaf Aug 08 '16 at 11:12
3

On Windows platforms, you can shell out to query whether an installed JRE exists:

java -version:1.6 -version

That command will come back with "java version "1.6.0_xx"" if you have it installed. If you don't have it installed, it'll say, "Unable to locate JRE meeting specification 1.6"

This doesn't seem to work on Linux, probably because Linux has no standard way to install Java.

Troy
  • 41
  • 2
  • Looks like this may only work for the Hotspot JVM and only detects installs with the same bits (32/64) as the version of Java being used to run the command – Brad Mace Mar 21 '22 at 14:15
3

I did a quick check of the windows registry, and this key seems to provide the various Java version installed on the system

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment

On further google search, it was confirmed that this is the correct location. Read these articles for the details...

Quickly retrieve available Java JVM on a workstation (Windows)

Java 2 Runtime Environment for Microsoft Windows

vikramsjn
  • 1,013
  • 1
  • 10
  • 21
2

In Windows Command prompt you could run the following 2 commands (or run together as a batch file). This will look in Windows Add/Remove programs (registry entries) and report which versions of JAVA are found with install/uninstall links, along with folder path, and some other things.

wmic product where "name LIKE '%%java%%'" get * /format:textvaluelist > temp_javavers.txt 

notepad.exe temp_javavers.txt
madth3
  • 7,275
  • 12
  • 50
  • 74
Craig
  • 21
  • 1
2

Basically there is no way to enumerate All JVM even not from Java code. Consider different platforms, diffent JDK vendor, bundled JDK in other products from databases to flight simulator and video rendering engine. Most of JDK are simple link to another, e.g. v.1.2 pointed to installed v.6 . The common thing is how to get a version : java -version But we have some points:

  1. For Windows look at registry HKEY_LOCAL_MACHINE\Software\JavaSoft\ . Also consider Wow6432Node mode for 64-bit OS. Check products from main vendors whose create software on Java.
  2. Mac OS X /System/Library/Frameworks/JavaVM.framework/Versions + Open JDK
  3. Linux and some other Unix - which java, /etc/alternatives/java rpm -qa|grep java , etc.
Sergei Chicherin
  • 2,031
  • 1
  • 18
  • 24
1

installed is pretty vague - even if JREs and JDKs are shipped with an installer, in fact we simply need to copy the files of a JRE or JDK to a machine and can use it right away.

So in general you'd have to find all java or java.exe executables on your local machine and call java -version to see1, if this executable named java/ java.exe really is (part of) a JRE.


Just saw, that you asked for JVM and want to call the compiler.. If you're looking for JDKs, use the above method but find all javac/javac.exe. They have a -version option too.


1there's a no risk - no fun that comes with this method - please have a close look at Sean's comment! If you can't trust the machine (or its users), then you might want to test, if the executable is a script/batch or a binary - even though a binary can wipe your disk too, even the original javac executable can be replaced with some evil-doing-code...

Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • 4
    *find all java or java.exe executables on your local machine and call java -version* That's horrible. You can't just go around executing binaries all over the system! What if some wise guy places a shell script named `java` with contents `rm -rf /` somewhere? – Sean Patrick Floyd Jun 27 '11 at 13:59
  • 1
    @Sean Patrick Floyd - but that's a general dilemma: unless you don't know the checksums of all original java/javac executables, you'll *have* to execute the executable to see, if it really is a java/javac binary... knowing, that the test can blow up your system (or add some tiny malware). – Andreas Dolk Jun 27 '11 at 14:09
0

As previously said, HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment should list available JVMs, BUT I just found out that while this registry is currently only listing a 64-bit 1.6.0_31 JVM on my machine, I also have a 32-bit java installed under C:\Windows\SysWOW64.

So the information listed on the registry does not paint the full picture, and if you want to run a 32-bit JVM in a 64-bit Windows, you should also try C:\Windows\SysWOW64.

Petrucio
  • 5,491
  • 1
  • 34
  • 33
0

I am not sure if this answers your question, but you can control which major JRE version an applet or WebStart application will run on using the Family Versioning feature. This allows you to specify the major Java release that an applet runs on. You should be able to derive the location of javac from there.

http://www.oracle.com/technetwork/java/javase/family-clsid-140615.html

maple_shaft
  • 10,435
  • 6
  • 46
  • 74
  • That advice is quite old & specific to applets. Check here for details of specifying a [JWS version](http://pscode.org/jws/version.html). For applets, I'd tend to use [deployJava.js](http://download.oracle.com/javase/6/docs/technotes/guides/jweb/deployment_advice.html) to ensure a minimum Java is available. `deployJava.js` can also be used for writing the launch button for JWS apps. – Andrew Thompson Jun 27 '11 at 14:43
0

..compile some source code using a specific JDK version.

Use the JavaCompiler (in the latest JDK the user can lay their hands on) with appropriate options for -source, -target & -bootclasspath. The last two are part of the Cross-Compilation Options of javac.

As to finding the JDK, pop a JFileChooser with the path of the current JRE as the default directory. If the user cannot navigate from there to a JDK, it is doubtful they should be writing code.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
0

To find the versions of Java installed, a better approach than looking for javac.exe is to check the registry. Java creates a registry key HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment with a string CurrentVersion set to the version number, for example 1.5 or 1.6.

You can use that information to find the JVMs:

  • HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\1.5\JavaHome = C:\Program Files\Java\j2re1.5
  • HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\1.5\RuntimeLib = C:\Program Files\Java\j2re1.4.2\bin\client\jvm.dll

You can see more here: http://java.sun.com/j2se/1.4.2/runtime_win32.html

I am not familiar with accessing the registry from Java, but you can always run regedit's command line interface and parse the results, which is what I have done.

Steven
  • 920
  • 2
  • 9
  • 21
  • @Andrew The java.sun.com page lists info for Windows and Solaris, but not Mac or Unix. Here's a page on [how to find Java on Macs](http://developer.apple.com/library/mac/#qa/qa1170/_index.html). On Unix it may require you to check folders for the JVM itself; I'm not sure. – Steven Jun 28 '11 at 14:10