Start separate process to run Java program using same JRE - java

I want to start a separate process from my java program to run another java program using same JRE that the current java program is executing in. Normally, I could get the path to the java executable using System.getProperty, but the java program is running in a bundled jre (Mac app package) which doesn't actually contain a java executable. Therefore, I'm wondering if there is there any API to directly run a Java program in a separate process?

Javapackager from Java version 9 on includes the bundler argument -strip-native-commands which leaves the executables in the bundled JRE. Just include the option:
-Bstrip-native-commands=false

The API is public hosted here: http://docs.oracle.com/javase/8/docs/api/
and the information you are looking for cons from the System utility class:
All available properties are listed here: http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getProperties--
The current JVMs location is available via "java.home".
So what your looking for is:
String javaPath = new File( System.getProperty("java.home"),"bin/java").absolutePath();

This may give a better picture.
Get the Java executable using below.
System.getProperty("java.home") + "/bin/java"
ReConstruct the class path,
((URLClassLoader() Thread.currentThread().getContextClassLoader()).getURL()
From here, you can start the new process using
Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_WIH_MAIN)

Related

Java code - Best way to list java processes in localhost JVM (for Linux & Windows]

I'm writing a console app that will run in Linux and Windows systems. This app will mainly run on JRE 1.8 environments.
In my code, it needs to get a list of Java processes that run on the host running my application. I have read the posts about jps, which come as a part of JDK, but not JRE, and also it's a separate executable.
My question is, is there any java library that I can use, to list (and ideally also kill some of) the running Java processes? Preferably, I would not want to hassle of calling jps executable with sth like Runtime.exec().
Thanks in advance, for any suggestions.
Oops: I just spotted that you need solutions for JRE1.8, this will only help for JRE9+.
A simple way to scan processes within JRE is to use ProcessHandle.allProcesses(). This will read all processes and you can add filter on simple name match with "java" or "java.exe":
Path java = Path.of("bin", "java.exe"); // use "java" if not on Windows
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command().isPresent())
.filter(ph -> Path.of(ph.info().command().get()).endsWith(java))
.forEach(ph -> {
System.out.println("PID "+ph.pid()+" "+ph.info().command());
})
This approach won't spot Java VMs embedded in EXE - such as from jpackage - so is not as reliable as using JDK jps which does recognise jpackage EXEs and JDK tools like jshell.exe.
I finally found the 3rd party library named Oshi, which apparently does not need JDK and is compatible with Java 1.8: https://github.com/oshi/oshi
For future reference, here is a sample method:
public static List<OSProcess> getOSProcesses() {
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
return os.getProcesses();
}

How to add capabilities to a native library but not to the executable running it?

Context
I've done a java library that uses a C library with JNI.
The C library is compiled in linux into a .so file. This library needs cap_net_raw capabilities.
Goal
Execute a java process without additional privileges, that uses said java library. The actual processes that are going to use the library are existing processes already in prod and we don't want to give them more rights.
To test that, I've created a jar and run it both with and without sudo. As expected, it succeeds with but fail without it.
Steps to reproduce the test
Create a java class with a native method, let's call it SocketTester.java
static {
System.loadLibrary("SocketTester");
}
private native int socketTest();
Generate socketTester.h file with the command
javac -h . SocketTester.java
Create socketTester.c file that implements socketTester.h and which needs the cap_net_raw capabitily
Compile with
gcc -o libSocketTester.so socketTester.c -shared -I/usr/lib/jvm/java-14-openjdk-amd64/include -I/usr/lib/jvm/java-14-openjdk-amd64/include/linux
Move libSocketTester.so to /usr/lib
Run
sudo ldconfig
Set the cap
cd /usr/lib
sudo setcap cap_net_raw=epi libSocketTester.so
Create a Test.java class
public static void main(final String[] args) {
SocketTester tester = new SocketTester();
tester.socketTest();
}
Create a jar with SocketTester.java and Test.java
Run the test
java -cp socketTester.jar Test
What I've already tried
Adding cap to the .so lib
sudo setcap cap_net_raw=epi libSocketTester.so
Result: Failure
Adding cap to java
sudo setcap cap_net_raw=epi /usr/lib/jvm/java-14-openjdk-amd64/bin/java
Result: It works, but it's not what I want because now all java process have the capability (see bold in goal section).
The question
Why is adding the cap to the .so doesn't work? How else can I accomplish the goal?
A zillion years ago I figured out how to have PAM modules fork a helper program to do privileged things from unprivileged contexts. This is how pam_unix.so is able to invoke unix_chkpwd to help an unprivileged application (a screensaver, or screen) accept a user password to unlock under Linux.
More recently, I learned the trick to making shared library objects (libcap.so, pam_cap.so etc) work as standalone binaries. I've since been thinking about combining both of these techniques... Researching that, I came across this question. Since I was able to do it for the example task of an unprivileged program binding to port 80, I thought it might be of interest as an answer here.
I've done a full write up of how it works on the Fully Capable libcap distribution site, but it essentially boils down to three things:
make the .so file executable as a stand alone program, with its own file capability
include some code in the .so file that figures out its own filename when linked into another program (this uses a _GNU_SOURCE extension: dladdr())
create some mechanism for the library itself to fork/exec itself (I use libcap:cap_launch()) with a private communication mechanism (I use a Unix domain socket generated with socketpair()) back to the app-linked shared library code.
The flow is basically, app calls the .so function, that function invokes the .so file as a forked child and performs the privileged operation. It then returns the result to the app over the Unix domain socket and exits.

Get JRE version from application

I need to get the java version from my application that the user has currently installed on their machine. However, the application that I have is installed with a self-contained JRE and when I do a System.getProperty("java.version"), it only returns the version of that self-contained JRE. Is there anyway that I can get the version that is installed on the machine?
The JRE installation are listed in the registry in the key only for Windows, Linux do not have central registry.
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment
You can make a simple program to test :
public class ShowVersion {
public static void main(String args[]) {
System.out.println(System.getProperty("java.version"));
}
}
Or you can try command prompt
Type java -version
For more you can refer sister site of SO :
https://superuser.com/questions/1221096/how-do-i-check-what-version-of-java-i-have-installed
if you're bundling a JRE (did you check the distribution license for it? Make sure you are allowed to do so) and it's running under that, you get that version back.
If the user were to run it under another JRE, you'd get the version of that JRE. That's just how things work.
In fact if you're using a self-contained JRE the user doesn't even have to have another JRE installed on his system at all, that's the entire point of bundling one in the first place.
Apart from a full file system scan it's impossible to know what other JVMs might be installed, and if you do that you'd have to account for all the different names the Java runtime executables may have depending on the files system you're running on. And after finding all those executables you still have no real way of knowing what version of Java they belong to unless you either do a binary analysis of the executables or somehow detect the information from other files in the directories where those executables are installed, files that may or may not be present depending on the system in question, how it was set up, what JVM is in use, and whether the installation has been manually altered or not.
For example, in the root directory of a JDK installation there is a file called "release" which contains the JVM version, but AFAIK that file isn't required to be there for the JVM to work properly. And your application may not have the rights to the file system to read that file.
You can try looking in known paths e.g. C:\Program Files\Java but unless you scan the entire file system for java or java.exe you will never be sure.
This might not work as expected as in the user that runs your application shouldn't have access to system directories. You can force you application to be started with administrator level access but that would be a security risk.
You requirement seems a bit pointless if you are already bundling a JRE with your application.
You can run cmd command using java (java -version) and use that output to get version.
List<String> cmdArgs = new ArrayList<>();
cmdArgs.add("cmd.exe");
cmdArgs.add("/C");
cmdArgs.add("java -version");
ProcessBuilder processBuilderObj = new ProcessBuilder();
processBuilderObj.command(cmdArgs);
Process process = processBuilderObj.start();
//get inputStream
BufferedReader data = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line1;
while ((line = data.readLine()) != null){
System.out.println(line);
}
While System.getProperty("java.version") returns the version of the Java that your app is running on, calling "java -version" as a system command from your Java app will return the version of the default(installed) Java in your system.
Here are the generic steps to find it programmatically:
Using ProcessBuilder in Java, execute the system command "java -version". This is certainly platform-independent.
Read the output from executed process. The version is usually on the 1st line of the output (smth like java version "1.8.0_191"), so reading the 1st line is enough.
Check using regular expression, if the output matches a string containing a java version, you just parse the version and that will be what you are looking for.
Otherwise, it means the output is an error message (smth like -bash: java: command not found), and there is no Java installed in your system.
control panel
java
Java tab
view
you can see exact version of java used and it's location.

If javac was written in Java, why I can execute javac as if it is a none-java program? [duplicate]

This question already has an answer here:
Why Java compiler as distributed as executable and not as JVM bytecode?
(1 answer)
Closed 7 years ago.
Java program needs to be packaged to JAR file so it can be executed using java -jar command. So why don't I have to execute javac with java -jar javac command? How did Sun/Oracle make java program into executable binary file?
I know there are tools that can convert jar file to windows executable file. But I want my jars to be executable in Linux/OS X without the help of bash script.
---------- UPDATE
I found this link really helpful: https://github.com/maynooth/CS210/wiki/Convert-Java-Executable-to-Linux-Executable
If javac was written in Java, why I can execute javac as if it is a none-java program?
The answer to your question has already been given by Jon Skeet for the question Why Java compiler as distributed as executable and not as JVM bytecode?
Quoting his answer here below
javac.exe (on my installation, JDK 1.8 on Windows x64) is about 15K in size. This isn't the full compiler. The compiler itself really is written in Java, and javac.exe is just a launcher, effectively. This is true of many of the tools that come with Java - it would be a pain to have to run something like:
java -cp path/to/javac.jar java.tools.Javac -cp path/to/your/libraries Foo.java
A simple way to understand the whole thing is imagining JRE(Java runtime environment) acting like an intermediate layer between your program and the OS.
JRE accepts the bytecode and runs your java program.The javac (java compiler ) converts your java source code to platform neutral byte code(exceptions are there). I am not sure if java,javac,jre is written in java or not. But if they are,then they have to linked/loaded in a different way in different OS(platforms).
Now coming to how to run jar in windows and linux
Normally java code is converted to jar file. Then there will be two files(mostly along with jar file) to start the jar file , one for windows and one for linux
For example Apache tomcat has files (in same location)
startup.bat ==> to start program in windows.
startup.sh ==> to start program in linux.
Alternately you could convert jar to exe for windows.
For linux the link you specified is enough. The script is interpreted by command interpreter in linux and your jar file will be executed.
This link specifies different ways of executing a shell script in linux.
http://www.thegeekstuff.com/2010/07/execute-shell-script/
This link has code to run jar as bat
Run .jar from batch-file
To sum it up , your jar file can be platform independent but they way to start the jar file will differ for different platforms.

Is Java required to run some .NET based programs?

Introduction
One of the comments to this question is about using GnuWin's file command in order to check whether a program is using java or not. However GnuWin's file command indicates the following:
C:\Windows\system32>file "C:\Program Files\Eclipse Foundation\eclipse\eclipse.exe"
C:\Program Files\Eclipse Foundation\eclipse\eclipse.exe; PE32+ executable for MS
Windows (GUI) Mono/.Net assembly
C:\Windows\system32>
while java is required in order to run programs, e.g. Eclipse, ApacheDS, Apache Directory Studio and Tomcat.
The discussion regarding this question resulted in a suggestion to ask a question at StackOverflow regarding the relation between .NET based programs and Java processes.
Question
Why does GnuWin's File Command indicate that certain programs are .Net based, while these require Java in order to run?
Eclipse.exe is not a java program: it is actually a native win32 executable that serves simply to locate and launch the JVM with appropriate commandline parameters and the path to the JAR file that contains the actual Eclipse java executable.
See for example http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fmisc%2Flauncher.html for full details.

Categories