Fail to redirect native library's output from console

J

Jamaica R.

The DLL is c++ wrapping a Fortran subroutine, and called via JNI in
the Java Application. I need to read the standard output from Fortran
code, and display it to a dialog in Java Dialog. But using I/O
redirection :
System.setOut(new PrintStream(nativeDLL.getOutputStream()) ) can only
read Java's standout output, -- it doesn't work on my DLL's output...
Could you pls help me? Thanks in advance.

We can't use Process.getInputStream() since this is a DLL rather an
executable and the library is loaded without using Process, and a
class LoopedStreams is used to implement input stream.


public void run()
{

final LoopedStreams ls = new LoopedStreams();
PrintStream ps = new PrintStream(ls.getOutputStream());
System.setOut(ps);
System.setErr(ps);
try
{
LoadNativeDll.Init(m_command);
new LoadNativeDll(m_workingDir+"\\").start();
}
catch (Exception ex)
{//display ex
return;
}
final BufferedReader br = new BufferedReader(new
InputStreamReader(ls.getInputStream()));
new Thread(new Runnable()
{
public void run()
{
try
{
String line;
while ((line = br.readLine())!=null)
{
//feed the line to a dialog
sleep(10);
}
fireStatusEvent(this,
RegressionStatusEvent.REGRESSSION_STOP);
}
catch (Exception ex)
{//display ex
return;
}

}
}).start();

}

// now the LoopedStreams wrapper
public class LoopedStreams {
private PipedOutputStream pipedOS =
new PipedOutputStream();

private boolean keepRunning = true;

private ByteArrayOutputStream byteArrayOS =
new ByteArrayOutputStream() {
public void close()
{
keepRunning = false;
try {
super.close();
pipedOS.close();
}
catch(IOException e) {
// record error and deal with it
// here simply stop it
System.err.println("Fail to close
ByteArrayOutputStream!");
return;

}
}
};


private PipedInputStream pipedIS = new PipedInputStream() {
public void close() {
keepRunning = false;
try {
super.close();
}
catch(IOException e) { return; }
}
};


public LoopedStreams() throws IOException {
pipedOS.connect(pipedIS);
startByteArrayReaderThread();
} // LoopedStreams()


public InputStream getInputStream() {
return pipedIS;
} // getInputStream()


public OutputStream getOutputStream() {
return byteArrayOS;
} // getOutputStream()


private void startByteArrayReaderThread() {
new Thread(new Runnable() {
public void run() {
while(keepRunning) {
// check the bytes size of the stream
if(byteArrayOS.size() > 0) {
byte[] buffer = null;
synchronized(byteArrayOS) {
buffer = byteArrayOS.toByteArray();
byteArrayOS.reset(); // flush the buffer
}
try {
// send data to PipedOutputStream
pipedOS.write(buffer, 0, buffer.length);
}
catch(IOException e) {
// record error and deal with it
// here simply stop it
System.err.println("Read thread
error!");
return;

}
}
else // no data and turn the thread to sleep
try {
// check the ByteArrayOutputStream for new
data every 1s
Thread.sleep(1000);
}
catch(InterruptedException e) {}
}
}
}).start();
} // startByteArrayReaderThread()
} // LoopedStreams
 
G

Gordon Beaton

The DLL is c++ wrapping a Fortran subroutine, and called via JNI in
the Java Application. I need to read the standard output from
Fortran code, and display it to a dialog in Java Dialog. But using
I/O redirection :
System.setOut(new PrintStream(nativeDLL.getOutputStream()) ) can
only read Java's standout output, -- it doesn't work on my DLL's
output... Could you pls help me? Thanks in advance.

We can't use Process.getInputStream() since this is a DLL rather an
executable and the library is loaded without using Process, and a
class LoopedStreams is used to implement input stream.

If the native code prints to stdout, then the output goes to stdout
regardless of what you do in Java. Note too that setOut() and similar
methods do not actually change the stdout of the JVM process.

You need to change the native code so that it returns its output or
posts it directly to the dialog, instead of printing it. Or put it in
a separate process so you can catch its output.

/gordon

--
 
J

Jamaica R.

Hi Gordon,

Thanks for the reply.
My native method is Fortran subroutine and wrapped into cpp library
for Java to use. Fortran code does generate large data printed to
stdout at random time.

An executable by the fortran routine was successfully redirected by
Process proc = (Runtime.getRuntime()).exec(m_commend);
BufferedReader in = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
which means Process's getInputStream() can obtain external routine's
stdout and my dedicated dialog can display them.

Now the same fortran subroutine is instead wrapped into cpp library
for Java to load with JNI technique. With the code posted above,
despite piped output/input and redirected system.out, fortran outputs
are seen in the Eclipse (my development environment) Console panel,
not my dedicated display dialog.

So maybe it's not fault of my native code. The problem is how to get
the DLL stdout, or how to use Process with a DLL...


Best Rdgs,
Jamaica
 
C

Chris Smith

Jamaica R. said:
An executable by the fortran routine was successfully redirected by
Process proc = (Runtime.getRuntime()).exec(m_commend);
BufferedReader in = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
which means Process's getInputStream() can obtain external routine's
stdout and my dedicated dialog can display them.

Right. When you execute a subprocess, Java automatically captures the
standard input, output, and error streams of that other process and
gives you access to them.

That is fundamentally different from running native code in your own
process. That native code uses your process's standard streams. So
you've got two options:

1. Run the Fortran code as a separate process.

2. Change something so that the Fortran code isn't really printing to
standard output. This may be as simple as calling freopen from the C++
wrapper.
 
R

rebecca.shuang

So you've got two options:
1. Run the Fortran code as a separate process.

2. Change something so that the Fortran code isn't really printing to
standard output. This may be as simple as calling freopen from the C++
wrapper.

Chris Smith,

Thank you.

I am currently trying to understand the difference between thread and
process. Is it true that Process can only be invoked from an
executable command but never from a dynamic library?

By "1. Run the Fortran code as a separate process. ", do you mean I
have to change the fortran code into an ".exe"?

For "2. Change something so that the Fortran code isn't really
printing to stdout", do you mean modify fortran code to write to a
file, or return a string to C and then to Java?
If so, I don't think it is applicable because the data to print might
be thousands of lines spending tons of time, so share a string is not
preferred. And I think that concurrent write and read access for a
file is not permitted in Windows.

Great appreciated if you could you please provide further
instruction...

Regards,
Jamaica
 
C

Chris Smith

I am currently trying to understand the difference between thread and
process. Is it true that Process can only be invoked from an
executable command but never from a dynamic library?

In general, it's true that the process you invoke will get its code from
an executable file -- possibly in conjunction with one or more dynamic
libraries. You can't invoke a dynamic library as its own process. (I'm
ignoring a certain complicated exception to this rule on the Windows
operating system; you should, too.)

If you really meant what you said -- invoked "from" -- then you're
incorrect. It doesn't matter whether the code that actually invokes the
process is in a library or not.
By "1. Run the Fortran code as a separate process. ", do you mean I
have to change the fortran code into an ".exe"?

Either that or write another program to call it. That other program
could even be written in Java; what matters is that it's a separate
process, so you can get its standard output as a stream.
For "2. Change something so that the Fortran code isn't really
printing to stdout", do you mean modify fortran code to write to a
file, or return a string to C and then to Java?

It's a general strategy. Use a file, or a string, or a named pipe, or
whatever. The point is that you can't redirect your Fortran code's
stdout from Java, so you'll have to do something in either the C++ or
Fortran code.
 
J

Jamaica R.

In general, it's true that the process you invoke will get its code from
an executable file -- possibly in conjunction with one or more dynamic
libraries. You can't invoke a dynamic library as its own process. (I'm
ignoring a certain complicated exception to this rule on the Windows
operating system; you should, too.)

If you really meant what you said -- invoked "from" -- then you're
incorrect. It doesn't matter whether the code that actually invokes the
process is in a library or not.


Either that or write another program to call it. That other program
could even be written in Java; what matters is that it's a separate
process, so you can get its standard output as a stream.


It's a general strategy. Use a file, or a string, or a named pipe, or
whatever. The point is that you can't redirect your Fortran code's
stdout from Java, so you'll have to do something in either the C++ or
Fortran code.

Thank you!
I'll try on a seperate simple Java app first...

Jamaica
 
P

Patricia Shanahan

Jamaica said:
Hi Gordon,

Thanks for the reply.
My native method is Fortran subroutine and wrapped into cpp library
for Java to use. Fortran code does generate large data printed to
stdout at random time.

An executable by the fortran routine was successfully redirected by
Process proc = (Runtime.getRuntime()).exec(m_commend);
BufferedReader in = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
which means Process's getInputStream() can obtain external routine's
stdout and my dedicated dialog can display them.

That can be implemented using pipes, without any special interception.

Telling a new job where to send its standard out is very different from
trying to change the rules for part of a running process.
So maybe it's not fault of my native code. The problem is how to get
the DLL stdout, or how to use Process with a DLL...

Just write a wrapper application, in any convenient language, that calls
the dll. The application's command line parameters can be used to tell
it initial parameters to pass to the dll. Your Java job can then use
ProcessBuilder and Process to control and communicate with the wrapper,
which in turn controls the dll.

Patricia
 
J

Jamaica R.

hi Patricia,

Thank you very much!
What you recommend is exactly what I finally managed to get through
today ^_^
The "wrapper" is written in an independent Java application. Then the
Process stands to help me out.

Jamaica
 
J

Jamaica R.

hi Patricia,

Thank you very much!
What you recommend is exactly what I finally managed to get through
today ^_^
The "wrapper" is written in an independent Java application. Then the
Process stands to help me out.

Jamaica
 
Y

yosri

hi Patricia,

Thank you very much!
What you recommend is exactly what I finally managed to get through
today ^_^
The "wrapper" is written in an independent Java application. Then the
Process stands to help me out.

Jamaica

Hi Jamaica,

I encoutered the same problem as described this thread , can you tell
me more about the Wrapper that you used to solve the problem?

Thank you in advance.
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Jamaica said:
The DLL is c++ wrapping a Fortran subroutine, and called via JNI in
the Java Application. I need to read the standard output from Fortran
code, and display it to a dialog in Java Dialog. But using I/O
redirection :
System.setOut(new PrintStream(nativeDLL.getOutputStream()) ) can only
read Java's standout output, -- it doesn't work on my DLL's output...
Could you pls help me? Thanks in advance.

I can understand that you have found alternative solutions, but
if you want a solution for the original problem then see:
http://www.vajhoej.dk/arne/eksperten/jniredirect/

If the Fortran RTL are build on top of the C RTL, then
it should work for a C++ wrapper around Fortran as well.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top