package jde.debugger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Connects standard input/output/error from a debuggee process to
* Emacs. This is done using four threads: a first thread started by
* the {@link #initConnect initConnect} method, which waits for Emacs
* to connect to a specified port. When that connection is
* established, three threads for stdin, stdout and stderr are created
* and started. The threads continue until the {@link #shutdown
* shutdown} method is called.
*
* Created: Sun Feb 18 01:24:09 2001
*
* @author Paul Kinnucan
* @version $Revision: 1.7 $
*/
public class DebuggeeSIO implements Protocol {
Integer procID;
/** Socket connection to do i/o */
Socket m_sioSocket = null;
Thread standardIOConnectThread;
StandardInputProcessor standardInputProcessor;
StandardOutputProcessor standardOutputProcessor;
StandardErrorProcessor standardErrorProcessor;
StandardOutputWriter standardOutputWriter;
private Debugger m_debugger;
public DebuggeeSIO(Debugger debugger){
m_debugger = debugger;
procID = debugger.getProcID();
}
/**
* Launches a thread to connect the Emacs standard I/O buffer
* for the current process to the standard I/O of the process.
*
*
* This method creates a socket for the standard I/O connection.
* The thread waits for Emacs to connect to the standard I/O socket.
*
* @return Address of standard I/O socket.
* @exception JDEException if an error occurs
*/
public int initConnect(final Integer cmdId) throws JDEException {
JDE.signal(procID, MESSAGE, "initSIOConnect: starting standard I/O handshake.", QUOTE);
ServerSocket ss = null;
try {
ss = new ServerSocket(0);
} catch (IOException ex) {
throw new JDEException("Unable to create a server socket");
}
final ServerSocket sstmp = ss;
final int port = ss.getLocalPort();
standardIOConnectThread = new Thread("Standard I/O Thread for App #"+procID) {
public void run() {
try {
sstmp.setSoTimeout(15000);
// Note!!! Added to solve initial hang up problem
JDE.commandResult(cmdId, String.valueOf(port), CMD_OK, NOQUOTE);
JDE.signal(procID, MESSAGE, "Debugger waiting for Emacs to connect to app SIO port " +
port + ".", QUOTE);
m_sioSocket = sstmp.accept();
sstmp.close();
initTransport();
} catch (IOException ex) {
JDE.signal(procID, ERROR, "Gave up waiting for Emacs to connect to SIO port: " + port, QUOTE);
} catch (SecurityException ex1) {
JDE.signal(procID, ERROR, "Security exception occurred while connecting to app SIO port " +
port, QUOTE);
}
}
};
JDE.signal(procID, MESSAGE, "initSIOConnect: starting SIO connect thread.", QUOTE);
standardIOConnectThread.start();
return port;
}
/**
* Describe initTransport
method here.
*
*/
public void initTransport() {
JDE.signal(procID, MESSAGE, "Debugger connected to standard I/O socket.", QUOTE);
final Process process = m_debugger.getVM().process();
standardInputProcessor = new StandardInputProcessor(process.getOutputStream());
standardInputProcessor.start();
standardOutputWriter = new StandardOutputWriter(m_sioSocket);
standardOutputWriter.println("*** Process Standard I/O ***");
standardOutputProcessor = new StandardOutputProcessor(process.getInputStream());
standardOutputProcessor.start();
standardErrorProcessor = new StandardErrorProcessor(process.getErrorStream());
standardErrorProcessor.start();
}
public void shutdown() {
try {
if (m_sioSocket != null) {
m_sioSocket.close();
}
} catch (IOException e) {
} // end of try-catch
}
/**
* Reads standard input from Emacs and forwards it to the application.
*
* @author Paul Kinnucan
* @version 1.0
* @since 1.0
* @see Thread
*/
private class StandardInputProcessor extends Thread {
public StandardInputProcessor(final OutputStream toVmStream) {
super("Input Processor for App #"+m_debugger.getProcID());
toVM = new PrintStream(toVmStream, true);
try {
fromEmacs =
new BufferedReader(new InputStreamReader(m_sioSocket.getInputStream()));
}
catch (IOException ex1) {
JDE.signal(procID, ERROR, "Could not get standard input stream from Emacs.", QUOTE);
}
// setPriority(Thread.MAX_PRIORITY-1);
}
public void run() {
if (fromEmacs == null) return;
try {
String line;
while ((line = fromEmacs.readLine()) != null) {
toVM.println(line);
toVM.flush();
}
/* XXX - Petter: handle this later? it seems to already be taken out...
if (!proc.isShuttingDown()) {
try {
// m_sioSocket.close();
JDE.signal(procID, MESSAGE, "Process closed its standard input.");
} catch (Exception ex) {
JDE.signal(procID, MESSAGE, "Couldn't close socket to standard input.");
}
}
*/
} catch (IOException ex) {
/* XXX - Petter: handle this later? it seems to already be taken out...
if (!proc.isShuttingDown()) {
try {
// m_sioSocket.close();
JDE.signal(procID, ERROR, "Input error; application I/O closed");
} catch (Exception e) {
JDE.signal(procID, ERROR, "Input error; couldn't close application I/O");
}
}
*/
}
}
PrintStream toVM;
BufferedReader fromEmacs;
}
/**
* Writes a line to the socket connected to the
* standard I/O buffer maintained by Emacs for this
* application.
*
* This class is used by the StandardOutputProcessor
* and StandardErrorProcessor to forward the application's
* standard ouput and error output to Emacs.
*
* @author ""
* @version 1.0
* @since 1.0
*/
private class StandardOutputWriter {
public StandardOutputWriter(Socket m_sioSocket) {
if (m_sioSocket == null) {
JDE.signal(procID, ERROR, "Could not transport app output. " +
"Transport socket does not exist.", QUOTE);
return;
}
OutputStream toEmacsStream;
try {
toEmacsStream = m_sioSocket.getOutputStream();
if (toEmacsStream == null) {
JDE.signal(procID, ERROR, "Could not transport app output. Transport socket closed.", QUOTE);
return;
}
}
catch (IOException ex1) {
JDE.signal(procID, ERROR, "Could not transport app output. Transport socket closed.", QUOTE);
return;
}
toEmacs = new BufferedWriter(new OutputStreamWriter(toEmacsStream));
}
public void write(char[] cbuf, int len) {
if (toEmacs != null) {
try {
toEmacs.write(cbuf, 0, len);
toEmacs.flush();
}
catch (IOException ex1) {
JDE.signal(procID, ERROR, "I/O error: cannot write process output to Emacs.", QUOTE);
}
}
}
public void println(String line) {
try {
toEmacs.write(line);
toEmacs.newLine();
} catch (IOException e) {
JDE.signal(procID, ERROR, "I/O error: cannot write process output to Emacs.", QUOTE);
} // end of try-catch
}
BufferedWriter toEmacs;
}
/**
* Forwards the application's standard output to Emacs.
*
* @author Paul Kinnucan
* @version 1.0
* @since 1.0
* @see Thread
*/
private class StandardOutputProcessor extends Thread {
public StandardOutputProcessor(InputStream fromVMStream) {
fromVM = new BufferedReader(new InputStreamReader(fromVMStream));
setPriority(Thread.MAX_PRIORITY-1);
}
public void run() {
String line;
try {
char[] cbuf = new char[256];
int len;
while ((len = fromVM.read(cbuf, 0, 256)) != -1) {
synchronized (standardOutputWriter) {
if (standardOutputWriter != null) {
standardOutputWriter.write(cbuf, len);
} // end of if ()
}
}
}
catch (IOException ex) {
}
/* XXX - Petter: handle this later? it seems to already be taken out...
if (!proc.isShuttingDown()) {
try {
// m_sioSocket.close();
JDE.signal(procID, MESSAGE, "Closed transport for application's standard output.");
} catch (Exception ex) {
JDE.signal(procID, ERROR, "Could not close application standard output transport.");
}
}
*/
}
BufferedReader fromVM;
}
/**
* Forwards the application's error output to Emacs.
*
* @author Paulk Kinnucan
* @version 1.0
* @since 1.0
* @see Thread
*/
private class StandardErrorProcessor extends Thread {
public StandardErrorProcessor(InputStream fromVMStream) {
super("Standard Error Processor for App #" + procID);
fromVM = new BufferedReader(new InputStreamReader(fromVMStream));
setPriority(Thread.MAX_PRIORITY-1);
}
public void run() {
String line;
try {
char[] cbuf = new char[256];
int len;
while ((len = fromVM.read(cbuf, 0, 256)) != -1) {
synchronized (standardOutputWriter) {
if (standardOutputWriter != null) {
standardOutputWriter.write(cbuf, len);
} // end of if ()
}
}
}
catch (IOException ex) {
}
/* XXX - Petter: handle this later? it seems to already be taken out...
if (!proc.isShuttingDown()) {
try {
// m_sioSocket.close();
JDE.signal(procID, MESSAGE, "Closed transport for application's standard error output.");
} catch (Exception ex) {
JDE.signal(procID, ERROR, "Could not close application standard error output transport.");
}
}
*/
}
BufferedReader fromVM;
}
}// DebuggerSIO
/*
* $Log: DebuggeeSIO.java,v $
* Revision 1.7 2003/01/15 05:49:22 paulk
* Add Petter's changes.
*
* Revision 1.6 2003/01/08 07:03:08 paulk
* Remove carriage returns.
*
* Revision 1.5 2003/01/08 06:53:38 paulk
* Integrate Petter Mahlen's updates.
*
*/
// End of DebuggerSIO.java