001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: * Contributor(s): Ivan Soleimanipour.
042: */
043:
044: package org.netbeans.lib.terminalemulator;
045:
046: import java.io.*;
047: import java.awt.*;
048: import javax.swing.SwingUtilities;
049:
050: public class StreamTerm extends Term {
051:
052: static class Fmt {
053: public static String pad(int what, int width) {
054: return pad("" + what, width);
055: }
056:
057: public static String pad(byte what, int width) {
058: return pad("" + (char) what, width);
059: }
060:
061: public static String pad0(String what, int width) {
062: return padwith(what, width, '0');
063: }
064:
065: public static String pad(String what, int width) {
066: return padwith(what, width, ' ');
067: }
068:
069: private static String padwith(String what, int width, char with) {
070: boolean left = false;
071: if (width < 0) {
072: left = true;
073: width = -width;
074: }
075: String sub;
076: if (what.length() > width)
077: sub = what.substring(0, width); // prevent overflow
078: else
079: sub = what;
080: int pad = width - sub.length();
081: StringBuffer buf = new StringBuffer(sub);
082: if (left) {
083: while (pad-- > 0)
084: buf.append(with);
085: } else {
086: while (pad-- > 0)
087: buf.insert(0, with);
088: }
089: return new String(buf);
090: }
091: }
092:
093: private OutputStreamWriter writer; // writes to child process
094:
095: /*
096: * Return the OutputStreamWriter used for writing to the child.
097: *
098: * This can be used to send characters to the child process explicitly
099: * as if they were typed at the keyboard.
100: */
101: public OutputStreamWriter getOutputStreamWriter() {
102: return writer;
103: }
104:
105: public StreamTerm() {
106: super ();
107:
108: addInputListener(new TermInputListener() {
109: public void sendChars(char c[], int offset, int count) {
110: if (writer == null)
111: return;
112: try {
113: writer.write(c, offset, count);
114: writer.flush();
115: } catch (Exception x) {
116: x.printStackTrace();
117: }
118: }
119:
120: public void sendChar(char c) {
121: if (writer == null)
122: return;
123: try {
124: writer.write(c);
125: // writer is buffered, need to use flush!
126: // perhaps SHOULD use an unbuffered writer?
127: // Also fix send_chars()
128: writer.flush();
129: } catch (Exception x) {
130: x.printStackTrace();
131: }
132: }
133: });
134: }
135:
136: /*
137: * Monitor output from process and forward to terminal
138: */
139: private class OutputMonitor extends Thread {
140: private static final int BUFSZ = 1024;
141: private char[] buf = new char[BUFSZ];
142: private Term term;
143: private InputStreamReader reader;
144:
145: OutputMonitor(InputStreamReader reader, Term term) {
146: super ("StreamTerm.OutputMonitor"); // NOI18N
147: this .reader = reader;
148: this .term = term;
149:
150: // Fix for bug 4921071
151: // NetBeans has many request processors running at P1 so
152: // a default priority of this thread will swamp all the RPs
153: // if we have a firehose sub-process.
154: setPriority(1);
155: }
156:
157: private void db_echo_receipt(char buf[], int offset, int count) {
158: /*
159: * Debugging function
160: */
161: System.out.println("Received:"); // NOI18N
162: final int width = 20;
163: int cx = 0;
164: while (cx < count) {
165: // print numbers
166: int cx0 = cx;
167: System.out.print(Fmt.pad(cx, 4) + ": "); // NOI18N
168: for (int x = 0; x < width && cx < count; cx++, x++) {
169: String hex = Integer.toHexString(buf[offset + cx]);
170: System.out.print(Fmt.pad0(hex, 2) + " "); // NOI18N
171: }
172: System.out.println();
173:
174: // print charcters
175: cx = cx0;
176: System.out.print(" "); // NOI18N
177: for (int x = 0; x < width && cx < count; cx++, x++) {
178: char c = (char) buf[offset + cx];
179: if (Character.isISOControl(c))
180: c = ' ';
181: System.out.print(Fmt.pad((byte) c, 2) + " "); // NOI18N
182: }
183: System.out.println();
184: }
185: }
186:
187: final private class Trampoline implements Runnable {
188: public int nread;
189:
190: public void run() {
191: term.putChars(buf, 0, nread);
192: }
193: }
194:
195: public void run() {
196: Trampoline tramp = new Trampoline();
197:
198: try {
199: while (true) {
200: int nread = reader.read(buf, 0, BUFSZ);
201: if (nread == -1) {
202: // This happens if someone closes the input stream,
203: // say the master end of the pty.
204: /* When we clean up this gets closed so it's not
205: always an error.
206: System.err.println("com.sun.spro.Term.OutputMonitor: " + // NOI18N
207: "Input stream closed");inp // NOI18N
208: */
209: break;
210: }
211: if (debugInput())
212: db_echo_receipt(buf, 0, nread);
213:
214: if (false) {
215: term.putChars(buf, 0, nread);
216: } else {
217: // InvokeAndWait() is surprisingly fast and
218: // eliminates one whole set of MT headaches.
219: tramp.nread = nread;
220: SwingUtilities.invokeAndWait(tramp);
221: }
222: }
223: reader.close();
224: } catch (Exception x) {
225: x.printStackTrace();
226: }
227: }
228: }
229:
230: /**
231: * Connect an I/O stream pair or triple to this Term.
232: *
233: * @param pin Input (and paste operations) to the sub-process.
234: * this stream.
235: * @param pout Main output from the sub-process. Stuff received via this
236: * stream will be rendered on the screen.
237: * @param perr Error output from process. May be null if the error stream
238: * is already absorbed into 'pout' as the case might be with
239: * ptys.
240: */
241: public void connect(OutputStream pin, InputStream pout,
242: InputStream perr) {
243:
244: // Now that we have a stream force resize notifications to be sent out.
245: updateTtySize();
246:
247: if (pin != null)
248: writer = new OutputStreamWriter(pin);
249:
250: InputStreamReader out_reader = new InputStreamReader(pout);
251: OutputMonitor out_monitor = new OutputMonitor(out_reader, this );
252: out_monitor.start();
253:
254: if (perr != null) {
255: InputStreamReader err_reader = new InputStreamReader(perr);
256: OutputMonitor err_monitor = new OutputMonitor(err_reader,
257: this);
258: err_monitor.start();
259: }
260: }
261:
262: }
|