Double fork from shell to run java process / jar - java

I have a Java application that needs to spawn another Java application. This second java application is long lived and might outlive the original java application. It's basically a big while loop that executes some networking code, and is not intended to terminate unless a specific condition happens. To launch this application, I use a bash script to double fork it, so that the original java application doesn't wait for it to complete:
How the original java app launches the shell script:
ProcessBuilder processBuilder = new ProcessBuilder(
"myshellscript",
"-a", "somearg",
"-b", "anotherarg");
final Process process = processBuilder.start();
process.waitFor();
if (process.exitValue() != 0)
{
// do something
}
The contents of the shell script:
fork() {
echo "Going to fork" >> /tmp/output.log
(setsid java -jar ./anotherapplication.jar "$#" &);
echo "Exited" >> /tmp/output.log
}
fork "$#"
In "output.log", I see "Going to fork" and "Exited" messages, but I never see any logs from the "anotherapplication.jar" app, which makes me think it doesn't start successfully. It also doesn't show in the output of ps aux either, so it definitely isn't running. Is this the right way to start the second application, or am I not starting it correctly?

Setsid should do what you want. I can't tell why your application doesn't start, but the application will not print anything to the /tmp/output.log, simply because you don't redirect output of the setsid line to the log file. I would redirect the stdout and stderr of that line to the output.log using:
(setsid java -jar ./anotherapplication.jar "$#" &) >>/tmp/output.log 2&>1
This will almost certainly show an error documenting why it fails to start. It's often a good idea to reset the standard input and output of daemon-like processes like this anyway.

Related

who i can run shell (.sh) scripts in Centos X64 and read output?

I'm using Intellij IDEA and i'm trying to run a shell script with arguments, and read the result of the execution.
this script is on my java SRC packge,
myScript.sh run a compiled c program
String[] cmd = { "/bin/bash", "-c", "myScript" };
Runtime.getRuntime().exec(cmd);
i resolved this by making a copy of myScript.sh in /ect/bin.
so this make my script as an environment path and give me the ability to read all the out put or add supplement arguments.
no changes has been made
on my Java code
.
Use ProcessBuilder:
Process process = new ProcessBuilder(cmd).start();
Then process.getInputStream() gives you access to the process standard output (stdout) which you can read as usual; process.getErrorStream() allows to read standard error (stderr).
Also you can do process.waitFor() to wait for the project to finish.
Note that to read anything from the stdout of a process you need to wait for the process to finish (or read it in a loop) and not just finish your main program.

Java launch independent process

I have a java app from which I run console based programs on linux system, I am reading the output of those programs and then my java app is sending it to a webpage.
But once I close my java app all the processes will get "stuck" or they just simply crash. So everytime I want to make some changes to my java app and I need to restart it I also have to close all processes that were running from my app. I would like to save their PIDs when closing my app and then take control (output streams) over those processes again based on saved PIDs of the processes.
Is there any way to do it?
I am running my programs like this:
ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "su", "-
s", "/bin/sh", "myuser", "-c", "java -jar myjar.jar" });,
Process p = processBuilder.start();
Edit:
My problem is not finding the process PID my problem is that my subprocesses lanched from my java app are crashing after my java app is closed/terminated and I need them to continue running even while my app is restarting/stopped.
Your problem is due to what is called Unix job control.
Like many shells do, /bin/sh intercepts SIGHUP and SIGINT signals, and before exiting, it sends signals to some of its child processes groups, depending on its configuration and on their state (for instance, stopped background processes receive a SIGCONT).
So, when your main java app is closed, the /bin/sh shell that your app had forked is terminated, and just before exiting, it sends a SIGHUP signal to its subprocesses corresponding to the command java -jar myjar.jar.
So, the answer to your question is: just use the huponexit /bin/sh shell option to avoid killing subprocesses. They will be detached from the controlling terminal, if any, but they will not be killed.
So, replace this java -jar myjar.jar by shopt -u huponexit; java -jar myjar.jar:
ProcessBuilder processBuilder =
new ProcessBuilder(new String[] {
"su", "-s", "/bin/sh", "myuser", "-c",
"shopt -u huponexit; java -jar myjar.jar"
});
Process p = processBuilder.start();
Try to run your command like this:
Runtime.getRuntime().exec("gedit");
It executes the specified string command in a separate process.
Some time ago I have found some useful information here. Try this.
I would use the ps command in Linux to get the number of each process(s) running which you want to control, of course you would execute it just as you have above with your ProcessBuilder. I would then pipe; "|" (Linux Command), the output into a file you have saved somewhere in your Java project.
The Linux command to execute from your Java program would look something a long the lines of
ps -A | grep "your_program_name" > /path/to/your/project/my_process_list_file.txt
Where the > stores the output of the command executed to your file.
I would then read from this file and execute some other Linux commands to take control of that process in whichever way you desire.
Good Luck, and happy coding my friend!

Send "sigterm" from Java to Bash script

I am starting a Java code from Bash script called start.sh. The Bash script fires up the Java code and then the Java code runs. At the end of the Java program, I want to send a signal back to the Bash script to terminate. Keep in mind that the Bash script runs with PID = 1. And I have to kill the PID 1 process.
I have the bash script set up such that it runs in an infinite loop and waits for a termination signal:
#!/bin/bash
# Run the java code here..
# Listen for an exit command.
trap 'exit 0' SIGTERM
while true; do :; done
I am using Docker instances and the signal is sigterm. I am following this tutorial: https://www.ctl.io/developers/blog/post/gracefully-stopping-docker-containers/ which explains how to send a sigterm from command line. I want to automate this process and close/kill the docker instance from inside as soon as the Java program ends.
How do I send the signal back to the bash script that started the Java code in the first place?
Should I follow
this method to send a signal as arguments to the bash script? Or will it start another bash script with a different PID (not 1).
Help needed!
Write 'set -e' in second line in bash script.
Dont use trap and while. Replace it by
'exec your_java_code_run'.
By this way docker get SIGTERM after java code run end.
Exemple:
#!/usr/bin/env bash
set -e
someOtherCodeIfNeed
exec your_java_code_run

Using Java to run Command Line commands in order

I'm writing a program that needs to be able to open and terminate a Minecraft server via command prompt. The code I have for it so far is this:
Runtime.getRuntime().exec("cd ./Server"); //go to the server directory
Process server = Runtime.getRuntime().exec("java -Xmx1024M -Xms1024M -jar minecraft_server.exe nogui"); //run minecraft server in nogui mode
Thread.sleep((long)(1000*60*.5)); //wait 30 seconds
Runtime.getRuntime().exec("stop"); //terminate the server
Thread.sleep((long)(1000*60*.5)); //wait 30 seconds
server.destroy(); //forcibly terminate the server assuming it hasn't already
System.exit(0);
This is just the basic logic code I have come up for it. I have tried using BufferedWriters but they just don't work and it gets an error even by doing the cd command. I am useing Minecraft_Server.exe which is at minecraft.net/download.jsp. The "stop" command runs while the process is active and tells the program to save the server and then terminate. Do I need to be using Threads in some way. Please help :) I've put several hours trying to find the answer already and I'm just clueless lol.
Your "cd ./Server" command, I think is going to lose context here, because the next call to exec isn't going to get the results of the first call. In other words, each call to exec is completely independent of other calls to exec.
Try putting some of this in a shell script instead, and simply exec the script. If you're controlling the server via the command line anyway, then this isn't any more complex than what you've already tried.
You might want to the look at the ProcessBuilder (here) where you can specify the working directory that will be used for all the command you want to execute after.
Each command is run as a separate process.
Your "cd ./Server" has no effect on the commands you run after.
Do you have a command line program called stop?
I would write
Thread.sleep(30 * 1000); // 30 Seconds.
and I wouldn't call System.exit(0);
To run command in particular dir use special version of exec
public Process exec(String command, String[] envp, File dir)
where dir is working dir for the process
Try putting the commands in a string array :
String[] cmd = { "cd","./Server" };
and
String[] cmd2 = {"java", "-Xmx1024M", "-Xms1024M", "-jar", "minecraft_server.exe", "nogui"}
Then try to
Runtime.getRuntime().exec(cmd); //go to the server directory
Process server = Runtime.getRuntime().exec(cmd2); //run minecraft server in nogui mode
Thread.sleep((long)(1000*60*.5)); //wait 30 seconds
Runtime.getRuntime().exec("stop"); //terminate the server
Thread.sleep((long)(1000*60*.5)); //wait 30 seconds
server.destroy(); //forcibly terminate the server assuming it hasn't already
System.exit(0);
Also stop will do nothing since you do not reference what to stop.

Java ProcessBuilder showing console of started java application?

I have a JAVA application that launches (using ProcessBuilder) another JAVA application like this:
String val = "something";
ProcessBuilder processBuilder = new ProcessBuilder("java", "-classpath", dir, appName, val);
Process p = processBuilder.start();
Now, this works fine, appName is launched with the parameter val and it runs and works ... great ... the problem is no Console Window appears ... appName does a LOT of outputting to the console and we need to see it ... how can I start the process with a console?
I am trying stuff like ("CMD.exe", "java", "-classpath", dir, appName, val), etc... but I can't get it right ...
Also, I can't redirect the streams, my program can actually start 5-10 of these appName's, each should have their own console window showing their own information.
Any help would be much appreciated.
Thanks,
console windows are generally not the most reliable form of logging. they only store a set amount of information (buffer) and can behave differently across platforms.
i strongly suggest logging to a file using something like log4j and if you need to see it real time use a tail like program (i see you're using windows).
in addition to this, seeing as you want the windows visible at all times and launching a tail program for each log might be annoying, i'd write my own log window in java swing.
the basic idea is to not rely on the OS too much.
Tried Runtime.getRuntime().exec("cscript java -classpath ..."); ?
Anyway, consider using a logging framwork (log4j, commons-logging), because opening 5 consoles is not the most clever thing to do.
I call a few shell scripts via Process to open a command line window and launch whatever I need. As long as the scripts don't detach - you can usually stop any shell command from doing this -java will still hold the running process.
I did it in linux but the concept should be similar.
#!/bin/bash
# To open a process in a new window.
gnome-terminal -x ./your-real-shell-script-here.sh "$#"
the real script will have your java execution in it, such as:
#!/bin/bash
java -jar your-jar-file.jar "$#"
I think you can use javaw to run on windows, so you might only need the one shell script.
A Console object only exists when you execute java.... from a console. Otherwise, the call to obtain one returns null.
If you want to see a console, you need to open a command shell console (e.g. windows cmd.exe or Unix bash shell window) and type:
java -classpath="..." com.example.appName arg1
If you want to run in a different manner, sorry to say, logging to Console is not for you. Instead, log using one of:
log4j
slf4j
logback

Categories

Resources