Check if tools.jar is available and load it dynamically during runtime - java

I'm working on a monitoring application, which uses Sigar for monitoring to monitor different kind of applications. One problem with Sigar is that when monitoring the heap usage of a Java application (JVM) I only get the maximum heap size but not the actually used heap size of the JVM.
So I extended my monitoring application to use JMX to connect to a JVM and retrieve the CPU as well as the heap usage. This works fine so far, but
I want to automise everything as much as possible and I don't want to start all my applications, being monitored, with JMX activated, but activate it dynamically when needed with the following piece of code:
private void connectToJVM(final String pid) throws IOException, AgentLoadException, AgentInitializationException {
List<VirtualMachineDescriptor> vms = VirtualMachine.list();
for (VirtualMachineDescriptor desc : vms) {
if (!desc.id().equals(pid)) {
continue;
}
VirtualMachine vm;
try {
vm = VirtualMachine.attach(desc);
} catch (AttachNotSupportedException e) {
continue;
}
Properties props = vm.getAgentProperties();
String connectorAddress = props.getProperty(CONNECTOR_ADDRESS);
if (connectorAddress == null) {
String agent = vm.getSystemProperties().getProperty("java.home") + File.separator + "lib"
+ File.separator + "management-agent.jar";
vm.loadAgent(agent);
// agent is started, get the connector address
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
}
vm.detach();
JMXServiceURL url = new JMXServiceURL(connectorAddress);
this.jmxConnector = JMXConnectorFactory.connect(url);
}
}
This works fine so far but the problem is that I have now a dependency to the tools.jar from the JDK.
My question is now can I somehow check during runtime if the tools.jar is available in the JAVA_HOME path and load it when it is? Because if it isn't available I just want to do the normal monitoring with Sigar, but if it is available I want to use JMX for monitoring Java applications.
My project is a maven project and I'm using the maven-shade-plugin to create a executable jar with all dependencies in it.
Currently I'm using a dirty hack I found in the internet which uses reflection to add the tools.jar dynamically to the system classpath if it exists. But I'm wondering if it is possible to do it differently as well?
Thanks in advance for your support.

I do a similar thing in my project, look here.
The idea is to load your utility class by differrent ClassLoader which has tools.jar in path.
File javaHome = new File(System.getProperty("java.home"));
String toolsPath = javaHome.getName().equalsIgnoreCase("jre") ? "../lib/tools.jar" : "lib/tools.jar";
URL[] urls = new URL[] {
getClass().getProtectionDomain().getCodeSource().getLocation(),
new File(javaHome, toolsPath).getCanonicalFile().toURI().toURL(),
};
URLClassLoader loader = new URLClassLoader(urls, null);
Class<?> utilityClass = loader.loadClass("some.package.MyUtilityClass");
utilityClass.getMethod("connect").invoke(null);

Finding tools.jar on the filesystem is a little more tricky than #apangin's solution.
Different JDK's stick the tools.jar in different places as shown by this method, which claims to support the IBM JDK and HotSpot on Mac.
But even the code I've referenced looks out of date. It suggests all mac JDK's use classes.jar, but my Mac 1.7 and 1.8 JDK's instead use tools.jar.
This other answer of mine shows locations of tools.jar and classes.jar files for mac some 1.6, 1.7 and 1.8 JDKs.

The code I ended up using is from: org.gridkit.lab::jvm Attach Api
https://mvnrepository.com/artifact/org.gridkit.lab/jvm-attach-api/1.2
Source code: http://central.maven.org/maven2/org/gridkit/lab/jvm-attach-api/1.2/
From that source code, you simply need one file: AttachAPI.java
/**
* Copyright 2013 Alexey Ragozin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gridkit.lab.jvm.attach;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* #author Alexey Ragozin (alexey.ragozin#gmail.com)
*/
class AttachAPI {
private static final LogStream LOG_ERROR = LogStream.error();
private static boolean started;
static {
try {
String javaHome = System.getProperty("java.home");
String toolsJarURL = "file:" + javaHome + "/../lib/tools.jar";
// Make addURL public
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
if (sysloader.getResourceAsStream("/com/sun/tools/attach/VirtualMachine.class") == null) {
method.invoke(sysloader, (Object) new URL(toolsJarURL));
Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine");
Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException");
}
} catch (Exception e) {
LOG_ERROR.log("Java home points to " + System.getProperty("java.home") + " make sure it is not a JRE path");
LOG_ERROR.log("Failed to add tools.jar to classpath", e);
}
started = true;
};
public static void ensureToolsJar() {
if (!started) {
LOG_ERROR.log("Attach API not initialized");
}
}
}
To use this class, put it somewhere in your project and ensure you change its package accordingly. In the example below, I have placed the file in the same folder as my MyApp.java file but I've not amended the AttachAPI.java file's package statement to reflect that since I wanted to leave it pristine.
Lastly, in your main class, ensure you have a block such as the follows:
public class MyApp
{
static {
AttachAPI.ensureToolsJar();
}
public static void ensureToolsJar() {
// do nothing, just ensure call to static initializer
}
}
...
Now you will no longer need to specify a path to the tools.jar on the command line and can launch you app with simply a java -jar MyApp.jar

Related

Finding default user.home when it has been overridden in Java VM options [duplicate]

What is the best way to find the user's home directory in Java?
The difficulty is that the solution should be cross-platform; it should work on Windows 2000, XP, Vista, OS X, Linux, and other Unix variants. I am looking for a snippet of code that can accomplish this for all platforms, and a way to detect the platform.
Per Java bug 4787931, system property user.home does not work correctly on Windows XP, so using this system property is not an acceptable solution as it is not cross-platform.
The bug you reference (bug 4787391) has been fixed in Java 8. Even if you are using an older version of Java, the System.getProperty("user.home") approach is probably still the best. The user.home approach seems to work in a very large number of cases. A 100% bulletproof solution on Windows is hard, because Windows has a shifting concept of what the home directory means.
If user.home isn't good enough for you I would suggest choosing a definition of home directory for windows and using it, getting the appropriate environment variable with System.getenv(String).
Actually with Java 8 the right way is to use:
System.getProperty("user.home");
The bug JDK-6519127 has been fixed and the "Incompatibilities between JDK 8 and JDK 7" section of the release notes states:
Area: Core Libs / java.lang
Synopsis
The steps used to determine the user's home directory on Windows have changed to follow the Microsoft recommended approach. This change
might be observable on older editions of Windows or where registry
settings or environment variables are set to other directories. Nature
of Incompatibility
behavioral RFE
6519127
Despite the question being old I leave this for future reference.
System.getProperty("user.home");
See the JavaDoc.
The concept of a HOME directory seems to be a bit vague when it comes to Windows. If the environment variables (HOMEDRIVE/HOMEPATH/USERPROFILE) aren't enough, you may have to resort to using native functions via JNI or JNA. SHGetFolderPath allows you to retrieve special folders, like My Documents (CSIDL_PERSONAL) or Local Settings\Application Data (CSIDL_LOCAL_APPDATA).
Sample JNA code:
public class PrintAppDataDir {
public static void main(String[] args) {
if (com.sun.jna.Platform.isWindows()) {
HWND hwndOwner = null;
int nFolder = Shell32.CSIDL_LOCAL_APPDATA;
HANDLE hToken = null;
int dwFlags = Shell32.SHGFP_TYPE_CURRENT;
char[] pszPath = new char[Shell32.MAX_PATH];
int hResult = Shell32.INSTANCE.SHGetFolderPath(hwndOwner, nFolder,
hToken, dwFlags, pszPath);
if (Shell32.S_OK == hResult) {
String path = new String(pszPath);
int len = path.indexOf('\0');
path = path.substring(0, len);
System.out.println(path);
} else {
System.err.println("Error: " + hResult);
}
}
}
private static Map<String, Object> OPTIONS = new HashMap<String, Object>();
static {
OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
OPTIONS.put(Library.OPTION_FUNCTION_MAPPER,
W32APIFunctionMapper.UNICODE);
}
static class HANDLE extends PointerType implements NativeMapped {
}
static class HWND extends HANDLE {
}
static interface Shell32 extends Library {
public static final int MAX_PATH = 260;
public static final int CSIDL_LOCAL_APPDATA = 0x001c;
public static final int SHGFP_TYPE_CURRENT = 0;
public static final int SHGFP_TYPE_DEFAULT = 1;
public static final int S_OK = 0;
static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32",
Shell32.class, OPTIONS);
/**
* see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx
*
* HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken,
* DWORD dwFlags, LPTSTR pszPath);
*/
public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken,
int dwFlags, char[] pszPath);
}
}
Others have answered the question before me but a useful program to print out all available properties is:
for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
System.out.println(String.format("%s = %s", e.getKey(), e.getValue()));
}
Alternative would be to use Apache CommonsIO FileUtils.getUserDirectory() instead of System.getProperty("user.home"). It will get you the same result and there is no chance to introduce a typo when specifying system property.
There is a big chance you already have Apache CommonsIO library in your project. Don't introduce it if you plan to use it only for getting user home directory.
As I was searching for Scala version, all I could find was McDowell's JNA code above. I include my Scala port here, as there currently isn't anywhere more appropriate.
import com.sun.jna.platform.win32._
object jna {
def getHome: java.io.File = {
if (!com.sun.jna.Platform.isWindows()) {
new java.io.File(System.getProperty("user.home"))
}
else {
val pszPath: Array[Char] = new Array[Char](WinDef.MAX_PATH)
new java.io.File(Shell32.INSTANCE.SHGetSpecialFolderPath(null, pszPath, ShlObj.CSIDL_MYDOCUMENTS, false) match {
case true => new String(pszPath.takeWhile(c => c != '\0'))
case _ => System.getProperty("user.home")
})
}
}
}
As with the Java version, you will need to add Java Native Access, including both jar files, to your referenced libraries.
It's nice to see that JNA now makes this much easier than when the original code was posted.
I would use the algorithm detailed in the bug report using System.getenv(String), and fallback to using the user.dir property if none of the environment variables indicated a valid existing directory. This should work cross-platform.
I think, under Windows, what you are really after is the user's notional "documents" directory.
If you want something that works well on windows there is a package called WinFoldersJava which wraps the native call to get the 'special' directories on Windows. We use it frequently and it works well.

UnsatisfiedLinkError: no opencv_java249 in java.library.path

Running into some problems making a piece of code run on my mac.
Had someone write me an image analysis java app but I keep getting this error when trying to run it on netbeans.
run: Exception in thread "main" java.lang.UnsatisfiedLinkError: no
opencv_java249 in java.library.path at
java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857) at
java.lang.Runtime.loadLibrary0(Runtime.java:870) at
java.lang.System.loadLibrary(System.java:1119) at
image.prossing.Test.main(Test.java:28) Java Result: 1 BUILD SUCCESSFUL
(total time: 0 seconds)
Have the netbeans project, and added the necessary jar files as libraries. The programmer told me to download the correct OpenCV version and copy the opencv.dll file to my java/jre/bin folder. But I cannot find the dll file or the java/jre folder.
I know most programming happens on windows for a reason. Hope someone can help me resolve this issue and run this application on my mac.
Here is the first part of the code, the part that is most probably creating the error:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package image.prossing;
/**
*
* #author Dumith Salinda
*/
import java.util.ArrayList;
import java.util.List;
import org.opencv.core.Core;
import static org.opencv.core.Core.FONT_HERSHEY_SIMPLEX;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
public class Test {
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Sorry if it's not that clear, let me know what info to add if something is missing or not clear.
Would truly appreciate any help you could give. Sincerely
Meir Warcel
Look into your OpenCV directory;
For an example this; (installed using brew install opencv3 --with-java --with-python3)
/usr/local/Cellar/opencv3/XXX/share/OpenCV/java
You will see;
libopencv_javaXXX.so opencv-XXX.jar
Now that you already have OpenCV's native library for Java (libopencv_javaXXX.so) compiled with you, the only thing left is, mac's dynamic library.
Link libopencv_javaXXX.so to libopencv_javaXXX.dylib;
ln -s libopencv_javaXXX.so libopencv_javaXXX.dylib
Now add /usr/local/Cellar/opencv3/XXX/share/OpenCV/java as Native Library Locations in IntelliJ or something similar in Eclipse.
Or add this to your JVM arguments;
-Djava.library.path=/usr/local/Cellar/opencv3/XXX/share/OpenCV/java
On a mac running OSX Yosemite, I dropped the libopencv_java2412.dylib file into /Library/Java/Extensions and it worked.
After you build opencv, the libopencv_java2412.dylib is generated in /build/lib.
After Spending a lots of time , and using different suggestions from StackOverflow I managed to get solution for windows. but I am adding a solution for mac as well. hope it should work.
Load your lib as per your system configuration.
private static void loadLibraries() {
try {
InputStream in = null;
File fileOut = null;
String osName = System.getProperty("os.name");
String opencvpath = System.getProperty("user.dir");
if(osName.startsWith("Windows")) {
int bitness = Integer.parseInt(System.getProperty("sun.arch.data.model"));
if(bitness == 32) {
opencvpath=opencvpath+"\\opencv\\x86\\";
}
else if (bitness == 64) {
opencvpath=opencvpath+"\\opencv\\x64\\";
} else {
opencvpath=opencvpath+"\\opencv\\x86\\";
}
}
else if(osName.equals("Mac OS X")){
opencvpath = opencvpath+"Your path to .dylib";
}
System.out.println(opencvpath);
System.load(opencvpath + Core.NATIVE_LIBRARY_NAME + ".dll");
} catch (Exception e) {
throw new RuntimeException("Failed to load opencv native library", e);
}
}
2.now use this method as per your need
public static void main(String[] args) {
loadLibraries();
}
Building on Harsh Vakharia's answer i tried installing OpenCV on my mac with macports:
sudo port install opencv +java
ls /opt/local/share/OpenCV/java
libopencv_java343.dylib opencv-343.jar
To use this library I was hoping to be able to modify the library path at runtime which was discussed in
Adding new paths for native libraries at runtime in Java
And ended up with the following helper class and unit test. The code is now part of the
Self Driving RC-Car open Source project in which I am a comitter.
JUnit Test
/**
* #see <a href=
* 'https://stackoverflow.com/questions/27088934/unsatisfiedlinkerror-no-opencv-java249-in-java-library-path/35112123#35112123'>OpenCV
* native libraries</a>
* #throws Exception
*/
#Test
public void testNativeLibrary() throws Exception {
if (debug)
System.out.println(String.format("trying to load native library %s",
Core.NATIVE_LIBRARY_NAME));
assertTrue(NativeLibrary.getNativeLibPath().isDirectory());
assertTrue(NativeLibrary.getNativeLib().isFile());
NativeLibrary.load();
}
NativeLibrary
package com.bitplan.opencv;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.opencv.core.Core;
/**
* load OpenCV NativeLibrary properly
*/
public class NativeLibrary {
protected static File nativeLibPath = new File("../lib");
/**
* get the native library path
*
* #return the file for the native library
*/
public static File getNativeLibPath() {
return nativeLibPath;
}
/**
* set the native library path
*
* #param pNativeLibPath
* - the library path to use
*/
public static void setNativeLibPath(File pNativeLibPath) {
nativeLibPath = pNativeLibPath;
}
/**
* get the current library path
*
* #return the current library path
*/
public static String getCurrentLibraryPath() {
return System.getProperty("java.library.path");
}
/**
* Adds the specified path to the java library path
*
* #param pathToAdd
* the path to add
* #throws Exception
* #see <a href=
* 'https://stackoverflow.com/questions/15409223/adding-new-paths-for-native-libraries-at-runtime-in-java'>Stackoverflow
* question how to add path entry to native library search path at
* runtime</a>
*/
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
// get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
// check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
// add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
public static File getNativeLib() {
File nativeLib = new File(getNativeLibPath(),
"lib" + Core.NATIVE_LIBRARY_NAME + ".dylib");
return nativeLib;
}
/**
* load the native library by adding the proper library path
*
* #throws Exception
* - if reflection access fails (e.g. in Java9/10)
*/
public static void load() throws Exception {
addLibraryPath(getNativeLibPath().getAbsolutePath());
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
}
Exception is occurring from below line of code:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Your program is trying to load a native library by the name of argument in call to loadLibrary method, which it is not able to locate. Make sure that native library (opencv.dll) is placed at one of the locations present in java.library.path system property as JVM looks at these locations for loading any native library (which might not contain 'java/jre/bin').
You can print java.library.path in your program like below:
System.out.println(System.getProperty("java.library.path"));
You cannot just put Windows library (dll file) on Mac and have it running - you need to compile the library for Mac first (or get Mac version of the library).
Please see here for tips on how to do it:
.dll Equivalent on Mac OS X
How do third-party libraries work in Objective-C and Xcode?
How to use a Windows DLL with Java in Mac OS X?
Instead of struggling with manual installation of OpenCV libraries I suggest you use OpenCV Java library packaged by OpenPnP (https://github.com/openpnp/opencv) that includes all required DLL.
It does not require additonal steps except of adding it to your build automation tool configuration (Gradle in my case) and adding the following code to load the library:
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
Just add into the path the folder where your opencv_java249.dll is; it would be something like C:\bin\opencv\build\java\x32 or C:\bin\opencv\build\java\x64 depending of your machine architecture. The problem is that java.library.path is actually the path variable.
netebans right klick project chosew properti
chose run, working direktory, click Browser change to opencv folder, release/lib,

Is there a Java library that searches for JVMs on the current machine? [duplicate]

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:
How to programatically get a Java version (Not default one) ?
How do I programatically get the path to the jdk / javac?
But I couldn't find what I was looking for.
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
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
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.
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:
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.
Mac OS X /System/Library/Frameworks/JavaVM.framework/Versions + Open JDK
Linux and some other Unix - which java, /etc/alternatives/java rpm -qa|grep java , etc.
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
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...
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
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.
..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.
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.

How to use SIGAR to remotely fetch another machine's system info?

I'm working on a project that requires to show the CPU usage as well as other system information of remote machines.
People suggest using SIGAR to achieve this, but I don't know how to use it. The source code didn't quite make sense to me.
Basically, my question is that: how can I register the MBeans provided by SIGAR to the server when host IP and JMX port are provided, and how to fetch the system info from other computer afterward.
Please correct me if I'm wrong with how JMX works. Thanks in advance.
These are the names of the classes that are the Sigar built in MBeans that you can register:
org.hyperic.sigar.jmx.SigarCpu
org.hyperic.sigar.jmx.SigarCpuInfo
org.hyperic.sigar.jmx.SigarCpuPerc
org.hyperic.sigar.jmx.SigarLoadAverage
org.hyperic.sigar.jmx.SigarMem
org.hyperic.sigar.jmx.SigarProcess
org.hyperic.sigar.jmx.SigarRegistry
org.hyperic.sigar.jmx.SigarSwap
However, it will be quite complicated to deploy these remotely since Sigar depends on a native library which must be in the target JVM's lib-path when the MBeans are loaded. This means, you will need to actively load the library and MBeans on each target host you want to monitor.
You might be able to hack a way of making the target JVMs load this through a remote call, but it is non-trivial and will require you to bypass any security setups in the JVMs since by default, this is something you're not supposed to be able to do.
You can sort of hack the system part to get an easy deployment for Sigjar:
private String before;
private Sigar sigar;
/**
* Constructor - don't forget to call unload later!
*/
public SetlogSigar() throws Exception {
before = System.getProperty("java.library.path");
String path = "";
String add = getJarFolder();
if (before.contains(";"))
path = before + ";./;" + add;
else
path = before + ":./:" + add;
setSystemPath(path);
sigar = new Sigar();
}
/**
* This is needed to dynamically update the JAVA Path environment in order to load the needed native library
* Yes -rather an ugly hack...
*/
private String getJarFolder() {
// get name and path
String path = SetlogSigar.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = path;
try {
decodedPath = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
File f = new File(decodedPath);
String absolutePath = f.getParentFile().getParentFile().getParentFile().getParent()+"/lib";
return absolutePath;
}
/**
* Unloads the JNI bindings
*/
public void unload() {
this.sigar.close();
setSystemPath(before);
}
This hack dynamically adds the folder where sigjar.jar is located to the environment variable. Just place all native libs in there and deployment gets less complicated.
Seems to me that you will have to write some wrapping objects to expose the various SIGAR outputs as JMX mbean attributes. How you do that that depends highly on what you are using to expose your JMX beans. I would write one wrapping object for each of the various different types of SIGAR output: memory, disk, ...
I've written a SimpleJMX library that might help. I'll use its format to provide an example object that you can use to expose the info via JMX. You can adapt it to whatever mechanism you are using to publish JMX means. I'm not familiar with SIGAR enough to know if my sigar code below is correct to get a ProcMem instance.
#JmxResource(description = "Show SIGAR Info", domainName = "foo")
public class SigarProcMem {
private ProcMem procMem;
{
// sorry, I'm not up on sigar so I'm not sure if this works
Sigar sigar = new Sigar();
procMem = sigar.getProcMem(sigar.getPid());
}
#JmxAttributeMethod(description = "Resident memory")
public long residentMemory() {
return procMem.getResident();
}
#JmxAttributeMethod(description = "Get the Total process virtual memory")
public long totalVirtualMemory() {
return procMem.getSize();
}
}

How to programatically get all Java JVM installed (Not default one) using Java?

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:
How to programatically get a Java version (Not default one) ?
How do I programatically get the path to the jdk / javac?
But I couldn't find what I was looking for.
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
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
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.
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:
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.
Mac OS X /System/Library/Frameworks/JavaVM.framework/Versions + Open JDK
Linux and some other Unix - which java, /etc/alternatives/java rpm -qa|grep java , etc.
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
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...
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
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.
..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.
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.

Categories

Resources