I have a java program:
public class ProcessMain {
public static final void main(String[] args) throws Exception {
Scanner keyboard = new Scanner(System.in);
boolean exit = false;
do
{ if(keyboard.hasNext()){
String input = keyboard.next();
System.out.println(input);
if( "abort".equals(input)){
ABORT();
exit = true;
}
}else{
System.out.println("Nothing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}while (!exit);
}
private static void ABORT(){
System.out.println("ABORT!!!!");
}
}
In Linux, a script:
rm testfifo
mkfifo testfifo
cat > testfifo &
echo $!
java -cp "Test.jar" com.example.ProcessMain < testfifo
Terminal A runs the script, "Nothing" can be printed every 5 seconds.
And then Terminal B execute echo "abort" >testfifo, but the program cannot display ABORT, it still displays Nothing every 5 seconds.
Please help!!
If you only need an external trigger to stop current processing. You might create a semaphore file and stop as soon it is created by another process.
See the following snippet.
// it will check for the file in the current directory
File semaphore = new File("abort.semaphore");
semaphore.deleteOnExit();
System.out.println("run until exist: " + semaphore);
while (!semaphore.exists()) {
System.out.println("Nothing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ABORT!!!!");
As long the file abort.semaphore does not exist, the program will print to the console and wait five seconds.
edit On Linux you might use a signal handler and send an SIGABRT to the running process.
the following snippet uses an internal proprietary API
import sun.misc.Signal;
import sun.misc.SignalHandler;
public class SigAbrt {
private static volatile boolean abort = false;
public static void main(String[] args) throws Exception {
Signal.handle(new Signal("ABRT"), new SignalHandler () {
public void handle(Signal sig) {
System.out.println("got a SIGABRT");
abort = true;
}
});
for(int i=0; i<100; i++) {
Thread.sleep(1000);
System.out.print('.');
if (abort) {
System.out.println("ABORT");
break;
}
}
}
}
run it
session one
java SigAbrt
session two
// first find the PID of SigAbrt
jps
example output of session two
2323 Jps
4242 SigAbrt
now send a SIGABRT to the SigAbrt process
kill -s SIGABRT 4242
example output of session one
...........got a SIGABRT
.ABORT
Program not printing on console might be because of your testfifo file is empty.
Try this:
printf "Hello\nMy\nFriend\nabort" > testfifo
java -cp "Test.jar" com.example.ProcessMain < testfifo
It will work.
Related
I want to realize some functions to automatically control the server. For instance, there's a command of Terraria dedicated server "Time" which shows the current time in game. I want to use another function dawn to reset the time at 12:00 AM in the game. So, I need to handle the input and output of the console server. To realize it, I'm using getRuntime() and I create 3 threads for the console server: inputstream, outputstream and errstream.
Here is my code for creation of the server process.
public static void main(String[] args) throws IOException, InterruptedException {
String path="F:\\Steam\\steamapps\\common\\Terraria\\TerrariaServer.exe";
Process p=Runtime.getRuntime().exec(path);
Thread errThread=new Thread(new ErrorCatcher(p.getErrorStream()));
Thread readThread=new Thread(new Reader(new BufferedReader(new InputStreamReader(p.getInputStream()))));
Thread writeThread=new Thread(new Sender(new BufferedWriter(new OutputStreamWriter(p.getOutputStream()))));
errThread.start();
readThread.start();
writeThread.start();
p.waitFor();
}
When directly running the server, it should look like this:
enter image description here
But after running it in the java:
- Terraria Server v1.4.0.5
1 Boss Training
2 Depths of Indignity
3 Fraternity of Gel
4 Powerful Defecation
5 Sule
6 The Dank Lake
n New World d
<number> Delete World
There are no such words "Choose World" and the process stops here. Process doesn't end and looks like gets into an endless loop. When entering some instructions to the server, there are not any responses. Then, I found there are 2 subprocesses under the main process when running the server, one is called "Terraria.exe" and another is "conhost.exe". When I use Task manager to detroy the Terraria.exe, the words "Choose World" appears but when sending commands, it shows the "java.io.IOException: Broken pipe". I guess that terraria.exe is the real server process and the conhost.exe is just a controler to show output and get input.
After that I added configrations of server to make the server directly start without choosing some options in the console.
Here is the code:
public static void main(String[] args) throws IOException, InterruptedException {
String path="cmd /c F:\\Steam\\steamapps\\common\\Terraria\\TerrariaServer.exe -config F:\\Steam\\steamapps\\common\\Terraria\\config1.txt";
Process p=Runtime.getRuntime().exec(path);
Thread errThread=new Thread(new ErrorCatcher(p.getErrorStream()));
Thread readThread=new Thread(new Reader(new BufferedReader(new InputStreamReader(p.getInputStream()))));
Thread writeThread=new Thread(new Sender(new BufferedWriter(new OutputStreamWriter(p.getOutputStream()))));
errThread.start();
readThread.start();
writeThread.start();
p.waitFor();
}
It works well, here are some information of the output:
Terraria Server v1.4.0.5
Listening on port 7777
Type 'help' for a list of commands.
: Server started
It looks like the server is running normally and I tried to play in the server, everything is ok.
But, still no responses when entering some instructions.
Here is the code of 3 threads(read,write and error):
read:
public static class Reader implements Runnable {
BufferedReader br;
Reader(BufferedReader br)
{
this.br=br;
}
#Override
public void run() {
String line="";
while(true)
{
try {
line=br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(line!=null) {
System.out.println(line);
}
}
}
}
err:
public static class ErrorCatcher implements Runnable{
InputStream error;
ErrorCatcher(InputStream error)
{
this.error=error;
}
#Override
public void run() {
try {
for (int i = 0; i < error.available(); i++) {
System.out.println("" + error.read());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
write:
public static class Sender implements Runnable{
BufferedWriter bw;
Sender(BufferedWriter bw)
{
this.bw=bw;
}
#Override
public void run() {
String cmd;
while(true)
{
try{
BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
cmd = bufferRead.readLine();
if(cmd.equals("quit"))
{
break;
}
bw.write(cmd+"\n");
bw.flush();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
I don't konw where the problem is. It seems like server can be controled by programm in this way. I also had an idea to get the subprocess of server - conhost.exe because I thought the problem is in the terraria.exe. But I didn't find the way to get the suprocess from its parent.
I am currently trying to create a scheduled task, that runs my Jar file every minute. the command line code to create the task looks like this:
schtasks /Create /tn Testname /sc Minute /tr C:\Users\MyUser\Desktop\Program.jar
no matter how long I wait, nothing happens. The Scheduled Task GUI shows that the result of my task is (0x1)
Howwever if I run C:\Users\MyUser\Desktop\Program.jar in the CMD everything works fine
What am I doing wrong?
I was able to create the scheduled task via Java, I dont know if this will help somebody in the future, but just in case I am gonna post my code anyway:
private void createScheduledTask(String taskName) {
List<String> commandList = new ArrayList<String>();
commandList.add("schtasks.exe");
commandList.add("/Create");
commandList.add("/tn");
commandList.add(taskName);
commandList.add("/sc");
commandList.add("Minute");
commandList.add("/tr");
commandList.add("java -jar C:\\Users\\MyUser\\Desktop\\Program.jar REPLACE_WITH_ARGUMENT");
try {
int returnValue = executeCMDStatement(commandList);
if(returnValue == 0){
//everything should have worked out
} else {
//there was an error
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static int executeCMDStatement(List<String> commandList) throws InterruptedException, IOException {
ProcessBuilder builder = new ProcessBuilder(commandList);
Process process = builder.start();
process.waitFor(3, TimeUnit.SECONDS);
return process.exitValue();
}
I am now executing java a java program like this:
package com.test;
public class Test {
public static void main(String[] args){
execute();
}
public static String execute(){
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "a";
}
}
I want to execute the Test.execute() method in linux shell script, wait until the method return and get return code . but the return of main() method is void , so what Can I do the get a return code or return msg from it ?
Any suggestions?
I find a solution:
package com.test;
public class Test {
public static void main(String[] args){
execute();
}
public static String execute(){
try {
System.out.println("sleeping");;
Thread.sleep(5000);
Runtime.getRuntime().exit(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "a";
}
}
And then my shell:
#!/bin/bash
java -cp test.jar com.test.Test
echo "The return code of the java application is $?"
I can get the value n which is in Runtime.getRuntime().exit(n);
First change the signature of main method in your code example:
public static void main() to public static void main(String[] args)
Then instead of just calling the execute method from main, try to print the result using System.out.println :
System.out.println(execute());
and then in linux shell you can use following to get the return values:
> set out = `java com.test.Test`
> echo $out
The shell script would have to call java com.test.Test. And this will call the main method which inturn is currently calling execute.
From a shell script you would have to start a JVM and a JVM always starts with a Main method.
As for the return code, you can access it using the $? shell variable.
So basically your shell script would be something like this:
#!/bin/bash
java -cp . com.test.Test
echo "The return code of the java application is $?"
Also you need to specify the classpath where all you relevant classes reside. In the above example I am putting in the current dir as the classpath.
The JVM will terminate with a exit code of 0 on completion of all non-daemon threads. If you want to return a specific exit code in case of an error you can use System.exit(<codehere>). Please note that calling System.exit() will cause the JVM to shutdown even if there are other non-daemon threads that are running.
Edit:
Added "-cp ." to the command based on the comments.
Added some exit code details
I want to run a jar script from within a Java program. The command is tested to run without problems if pasted sloley to the Windows cmd.
However, my Java script stucks at p.waitFor().
public class MyClass{
public static void main(String[] args) {
try {
// create a new process
System.out.println("Creating Process...");
Process p = Runtime.getRuntime().exec("U:\\locallib\\jdk-8u65-windows-x64\\tools\\bin\\java.exe -Xmx4g -jar U:\\path\\to\\my.jar arg1 arg2");
System.out.println("Process is running...");
p.waitFor();
System.out.println("Process is terminated...");
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
Ouput:
Creating Process...
Process is running...
...and nothing else.
Im currently writing a program that runs a class that implements runnable.
I have it so the time in a format of HH:MM:SS is printed to the screen every second.
Heres the code:
public class LaunchCounter
{
public static void main(String[] args)
{
//Runs the CounterThread
new CounterThread().start();
}
}
And here is the counter class
public class CounterThread implements Runnable
{
//Declare new thread
private Thread thread;
public void start()
{
thread = new Thread(this, "");
thread.start();
}
#Override
public void run()
{
//Formatter used to display just time not date
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
//never ending forloop to display time
for(int i = 1; i > 0; i++)
{
try
{
//Runtime.getRuntime().exec( "cmd /c cls" );
//Sleep for 1 second after each loop
Thread.sleep(1000);
//new calender is created
Calendar cal = Calendar.getInstance();
System.out.println(dateFormat.format(cal.getTime()));
}
catch(Exception e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
This works perfectly fine.
What i am trying to achieve is that the line that is printed is cleared after waiting a second, and the the new time is printed and so on.
So 12:00:01 becocomes 12:00:02 with out taking a new line.
I've tried System.out.print("\b\b\b\b\b\b\b") and Runtime.getRuntime().exec( "cmd /c cls" ); But this is just printing squares to the console.
How would i achieve this?
The problem is the terminal you're using. (My guess is that you are using the terminal in your IDE.) If your output terminal doesn't do full terminal emulation, it will either ignore the \b characters or display them as unprintable characters.
I tested the following code in IntelliJ IDEA 16 and verified that \b is ignored by the built in IDEA terminal. I then tested it in the MacOS terminal and it worked the way you want it to.
package test;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class CounterThread implements Runnable {
//Declare new thread
private Thread thread;
public void start() {
thread = new Thread(this, "");
thread.start();
}
#Override
public void run() {
//Formatter used to display just time not date
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
//never ending forloop to display time
for (int i = 1; i > 0; i++) {
try {
//Runtime.getRuntime().exec( "cmd /c cls" );
//Sleep for 1 second after each loop
Thread.sleep(1000);
//new calender is created
Calendar cal = Calendar.getInstance();
System.out.print("\b\b\b\b\b\b\b\b");
System.out.print(dateFormat.format(cal.getTime()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
//Runs the CounterThread
new CounterThread().start();
final Object monitor = new Object();
synchronized (monitor) {
monitor.wait();
}
}
}
You're on the right track using Runtime.getRuntime().exec("cls");. See this post here by Holger for maybe why you're not able to clear the console.
To solve this problem, we have to invoke the command line interpreter
(cmd) and tell it to execute a command (/c cls) which allows invoking
builtin commands. Further we have to directly connect its output
channel to the Java process’ output channel, which works starting with
Java 7, using inheritIO():
import java.io.IOException;
public class CLS {
public static void main(String... arg) throws IOException, InterruptedException {
new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
}
}
Now when the Java process is connected to a console, i.e. has been
started from a command line without output redirection, it will clear
the console.