String str = "";
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
int tempint = 0;
try {
//The program cannot get out from this while loop!
while((tempint = bfr.read()) != -1){
str += Character.toString((char)tempint);
}
}
catch(IOException ioe) {
System.out.println(ioe);
}
//Print the input
System.out.println(str);
This is the code I wrote for reading user's input from standard input. This piece of code is extracted as playinput.jar
And I have written a script play to run this jar. But after I run ./play in terminal(linux) and finish my input, which does not contain enter, I have to press Ctrl+D twice to get the input printed out.
The same problem occurred when I ran another script called check, which will simply invoke ./play and send some input via stdin. After I ran ./check, it just hanged there and the input cannot be printed out.
Could anyone help fix this problem? Thank you:)
That's just how the Linux terminal works. It has nothing to do with your Java code.
If you test it out with a command like cat > textfile, you will find that unless you are at the beginning of a line, ^D doesn't immediately end the file as you might expect it to. (I don't know all the details of this behavior, but that's the gist of it.)
The convention for Linux is that a text file always ends with a newline. You can run into problems like this if you don't follow the convention.
However, I'm not sure about your problem with the program hanging when you send it data using redirection. That part is more surprising to me, since it's not interactive so the terminal behavior shouldn't be an issue.
Related
I have a Java program running on linux that needs to be able to both set a users initial samba password, and then allow them to change their password without giving them access to the terminal.
Below is my code for changing the users password, as this is easier to test with, and I will be able to figure the other part out once I get this worked out.
The -s flag is supposed to allow stdin to be used.
String cmd = "smbpasswd -s -U user";
Process p = Runtime.getRuntime().exec(cmd);
OutputStreamWriter Out = new OutputStreamWriter(p.getOutputStream());
InputStreamReader In = new InputStreamReader(p.getInputStream());
BufferedWriter Write = new BufferedWriter(Out);
BufferedReader Read = new BufferedReader(In);
char[] output = null;
//I write all of the output lines to the log, but nothing is written, and the password doesn't change.
Read.read(output);
Write.write(OldPass);
Read.read(Output);
Write.write(NewPass);
Read.read(Output);
Write.write(NewPass);
Read.read(Output);
I need some help to figure out what I am doing wrong, and how I would go about this correctly. Any help is appreciated.
According to the man page for SMBPASSWD(8):
-s: This option causes smbpasswd to be silent (i.e. not issue prompts) and to read its old and new passwords from standard input, rather than
from /dev/tty (like the passwd(1) program does). This option is to aid
people writing scripts to drive smbpasswd
Emphasis on "not issue prompts". If I'm reading your code correctly, you seem to be waiting for prompts from the utility, which won't come (test it from the command line). But I may have misinterpreted your Java code.
I'm launching wkhtmltopdf from within my Java app (part of a Tomcat server, running in debug mode within Eclipse Helios on Win7 64-bit): I'd like to wait for it to complete, then Do More Stuff.
String cmd[] = {"wkhtmltopdf", htmlPathIn, pdfPathOut};
Process proc = Runtime.getRuntime().exec( cmd, null );
proc.waitFor();
But waitFor() never returns. I can still see the process in the Windows Task Manager (with the command line I passed to exec(): looks fine). AND IT WORKS. wkhtmltopdf produces the PDF I'd expect, right where I'd expect it. I can open it, rename it, whatever, even while the process is still running (before I manually terminate it).
From the command line, everything is fine:
c:\wrk>wkhtmltopdf C:\Temp\foo.html c:\wrk\foo.pdf
Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done
The process exits just fine, and life goes on.
So what is it about runtime.exec() that's causing wkhtmltopdf to never terminate?
I could grab proc.getInputStream() and look for "Done", but that's... vile. I want something that is more general.
I've calling exec() with and without a working directory. I've tried with and without an empty "env" array. No joy.
Why is my process hanging, and what can I do to fix it?
PS: I've tried this with a couple other command line apps, and they both exhibit the same behavior.
Further exec woes.
I'm trying to read standard out & error, without success. From the command line, I know there's supposed to be something remarkably like my command line experience, but when I read the input stream returned by proc.getInputStream(), I immediately get an EOL (-1, I'm using inputStream.read()).
I checked the JavaDoc for Process, and found this
The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the [b]subprocess to block, and even deadlock[/b].
Emphasis added. So I tried that. The first 'read()' on the Standard Out inputStream blocked until I killed the process...
WITH WKHTMLTOPDF
With the generic command line ap & no params so it should "dump usage and terminate", it sucks out the appropriate std::out, then terminates.
Interesting!
JVM version issue? I'm using 1.6.0_23. The latest is... v24. I just checked the change log and don't see anything promising, but I'll try updating anyway.
Okay. Don't let the Input Streams fill or they'll block. Check. .close() can also prevent this, but isn't terribly bright.
That works in general (including the generic command line apps I've tested).
In specific however, it falls down. It appears that wkhtmltopdf is using some terminal manipulation/cursor stuff to do an ASCII-graphic progress bar. I believe this is causing the inputStream to immediately return EOF rather than giving me the correct values.
Any ideas? Hardly a deal-breaker, but it would definitely be Nice To Have.
I had the same exact issue as you and I solved it. Here are my findings:
For some reason, the output from wkhtmltopdf goes to STDERR of the process and NOT STDOUT. I have verified this by calling wkhtmltopdf from Java as well as perl
So, for example in java, you would have to do:
//ProcessBuilder is the recommended way of creating processes since Java 1.5
//Runtime.getRuntime().exec() is deprecated. Do not use.
ProcessBuilder pb = new ProcessBuilder("wkhtmltopdf.exe", htmlFilePath, pdfFilePath);
Process process = pb.start();
BufferedReader errStreamReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
//not "process.getInputStream()"
String line = errStreamReader.readLine();
while(line != null)
{
System.out.println(line); //or whatever else
line = reader.readLine();
}
On a side note, if you spawn a process from java, you MUST read from the stdout and stderr streams (even if you do nothing with it) because otherwise the stream buffer will fill and the process will hang and never return.
To futureproof your code, just in case the devs of wkhtmltopdf decide to write to stdout, you can redirect stderr of the child process to stdout and read only one stream like this:
ProcessBuilder pb = new ProcessBuilder("wkhtmltopdf.exe", htmlFilePath, pdfFilePath);
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Actually, I do this in all the cases where I have to spawn an external process from java. That way I don't have to read two streams.
You should also read the streams of the spawned process in different threads if you dont want your main thread to block, since reading from streams is blocking.
Hope this helps.
UPDATE: I raised this issue in the project page and was replied that this is by design because wkhtmltopdf supports giving the actual pdf output in STDOUT. Please see the link for more details and java code.
A process has 3 streams: input, output and error. you can read both output and error stream at the same time using separate processes. see this question and its accepted answer and also this one for example.
You should read from the streams in a different thread.
final Semaphore semaphore = new Semaphore(numOfThreads);
final String whktmlExe = tmpwhktmlExePath;
int doccount = 0;
try{
File fileObject = new File(inputDir);
for(final File f : fileObject.listFiles()) {
if(f.getAbsolutePath().endsWith(".html")) {
doccount ++;
if(doccount >500 ) {
LOG.info(" done with conversion of 1000 docs exiting ");
break;
}
System.out.println(" inside for before "+semaphore.availablePermits());
semaphore.acquire();
System.out.println(" inside for after "+semaphore.availablePermits() + " ---" +f.getName());
new java.lang.Thread() {
public void run() {
try {
String F_ = f.getName().replaceAll(".html", ".pdf") ;
ProcessBuilder pb = new ProcessBuilder(whktmlExe , f.getAbsolutePath(), outPutDir + F_ .replaceAll(" ", "_") );//"wkhtmltopdf.exe", htmlFilePath, pdfFilePath);
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader errStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = errStreamReader.readLine();
while(line != null)
{
System.err.println(line); //or whatever else
line = errStreamReader.readLine();
}
System.out.println("after completion for ");
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(" in finally releasing ");
semaphore.release();
}
}
}.start();
}
}
}catch (Exception ex) {
LOG.error(" *** Error in pdf generation *** ", ex);
}
while (semaphore.availablePermits() < numOfThreads) {//till all threads finish
LOG.info( " Waiting for all threads to exit "+ semaphore.availablePermits() + " --- " +( numOfThreads - semaphore.availablePermits()));
java.lang.Thread.sleep(10000);
}
I'm trying to launch an external program from my java swing app using this:
Process proc = Runtime.getRuntime().exec(cmd);
But the external program never actually gets launched until I close out of my java app...everytime.
It waits to launch only after I have closed out.
the external program I am trying to run is an exe that takes arguments so:
cmd = "externalProgram.exe -v --fullscreen --nowing";
What could possibly be wrong here.
Funny enough it works as expected if i try something simple like:
Process proc = Runtime.getRuntime().exec("notepad.exe");
You may need to read from the process's standard output, or close the standard input, before it will proceed. For reading the output, the problem is that the buffer can get full, blocking the program; for closing the input, the problem is that some programs will try to read data from there if it's available, waiting to do so. One or both of these tricks is very likely to straighten things out for you.
You may also read the error output stream to check it the program is actually being unsuccessfully executed
String cmd = "svn.exe";
Process proc = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line = null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
reader.close();
My console shows
Type 'svn help' for usage.
Which evidently shows the program was executed by Java.
I am using Java on Windows XP and want to be able to send commands to another program such as telnet.
I do not want to simply execute another program. I want to execute it, and then send it a sequence of commands once it's running.
Here's my code of what I want to do, but it does not work:
(If you uncomment and change the command to "cmd" it works as expected. Please help.)
This is a simplified example. In production there will be many more commands sent, so please don't suggest calling "telnet localhost".
try
{
Runtime rt = Runtime.getRuntime();
String command = "telnet";
//command = "cmd";
Process pr = rt.exec(command);
BufferedReader processOutput = new BufferedReader(new InputStreamReader(pr.getInputStream()));
BufferedWriter processInput = new BufferedWriter(new OutputStreamWriter(pr.getOutputStream()));
String commandToSend = "open localhost\n";
//commandToSend = "dir\n" + "exit\n";
processInput.write(commandToSend);
processInput.flush();
int lineCounter = 0;
while(true)
{
String line = processOutput.readLine();
if(line == null) break;
System.out.println(++lineCounter + ": " + line);
}
processInput.close();
processOutput.close();
pr.waitFor();
}
catch(Exception x)
{
x.printStackTrace();
}
That looks OK, as it won't be producing that much output, but you should really read and write in separate threads so it doesn't fill up the buffer and block waiting you to read before you reach the next step.
So if it's reaching the point where you flush the command you send to it, find out whether the Windows telnet client supports receiving commands from standard input rather than a console by piping the text you're sending to its standard input to it in a command prompt.
For example, echo dir c:\ | cmd causes cmd to run, list the c: drive contents and exit, much the same behaviour as if you typed dir c:\ into the console. But echo open localhost | telnet causes telnet to clear the screen then exit, rather than behaving the same way as if you typed it into the console. As telnet needs to mask user input for passwords, it's quite likely that it's using the console API rather than reading from standard input. It's help doesn't list any command arguments to tell it to read from standard input, so maybe you need to use a telnet implementation which is better suited to scripting.
It's not directly an answer to your question, but...
Instead of using Runtime.exec() you should use a ProcessBuilder and redirect stderr to stdout (ProcessBuilder.redirectErrorStream(true)). Otherwise your process could block if it writes something to stderr (Windows doesn't like it when the output of a process isn't read).
If you want to control a telnet session programatically from Java, you might be able to use this Java telnet library... you can do the same things (open connections, send username/password, send commands and receive results) but without actually spawning a separate process.
You may take a look at the Telnet Ant task you can call it directly in your code with out having to use a build.xml file.
You can also take a look at the source code and see how they do it.
I dont know how to describe it well, but i will try. Ok, i want to be able to build my java program so that when it opens, it will look and work exactly as it does in the console. So it reads the Scanner class and prints normally, and does everything it would do if it was in the console. Ive looked around for this and havent found anything. I can make a gui java program fairly easily, but i would rather have a terminal, console like program, that works exactly as the java console, thanks.
Based on your comment:
but i want to do hundreds of links at
a time which works perfectly using the
scanner nextLine() method, i can just
post 100 or 200 links in the java
console and it will automatically sort
them out.
I'm guessing what you want is batch processing. You can have your 100 or 200 links in a text file, one per line. And then your Java program:
import java.io.*;
public class Batch{
public static void main(String args[]){
try{
FileInputStream fstream = new FileInputStream("sample.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
String line;
while((line = br.readLine()) != null){
//Do something with your line
System.out.println(line);
}
in.close();
}catch(IOException ioe){
System.err.println(ioe.getMessage());
}
}
}
You compile this program, open up a console and run it:
java Batch
It reads your sample.txt file and for each line it does something, in this case print it to the console.
You might be looking for the standard input and output members of the java.lang.System class:
class HelloWorld {
public static void main(String... argv) {
System.out.println("Hello, World!");
}
}
For processing input, you can use Scanner on standard input:
Scanner scanner = new Scanner(System.in);
If you want to get really fancy, you can print some of your output to System.err, which is a PrintStream just like System.out.
From the comment, "when i compile my classes i get a jar file, which does nothing when i click on it, which i think is normal because its not gui," I think your problem is an operating system problem (Windows?), not a Java problem.
Windows maps the "Open" action for JAR files to run with javaw.exe, which doesn't create a console. You'll either need to modify the default file association on each machine, or create something like a batch file that overrides this default behavior.
You could write two programs: the first is your actual "console" Java application, and another is just a shell that uses Runtime.exec() to create a Windows console (cmd) and executes the first program within it.
There are also opensource projects (check Sourceforge) that wrap your JAR in a Windows executable.
Use launch4j. It will create a exe of your non GUI application. In launch4j go to header section and check the console option. Done!