001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Jan Blok 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package com.izforge.izpack.util; 023 024import java.awt.Dimension; 025import java.awt.Font; 026import java.awt.Toolkit; 027import java.awt.event.KeyEvent; 028import java.awt.event.KeyListener; 029import java.io.BufferedOutputStream; 030import java.io.BufferedReader; 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.InputStreamReader; 034import java.io.OutputStreamWriter; 035import java.io.PipedInputStream; 036import java.io.PipedOutputStream; 037import java.io.PrintStream; 038import java.io.PrintWriter; 039 040import javax.swing.JFrame; 041import javax.swing.JScrollPane; 042import javax.swing.JTextArea; 043import javax.swing.SwingUtilities; 044import javax.swing.event.DocumentEvent; 045import javax.swing.event.DocumentListener; 046import javax.swing.text.Document; 047import javax.swing.text.Segment; 048 049public final class Console 050{ 051 052 public static final int INITIAL_WIDTH = 800; 053 054 public static final int INITIAL_HEIGHT = 600; 055 056 public static void main(String[] args) 057 { 058 Runtime rt = Runtime.getRuntime(); 059 Process p = null; 060 try 061 { 062 063 /* 064 * Start a new process in which to execute the commands in cmd, using the environment in 065 * env and use pwd as the current working directory. 066 */ 067 p = rt.exec(args);// , env, pwd); 068 new Console(p); 069 System.exit(p.exitValue()); 070 } 071 catch (IOException e) 072 { 073 /* 074 * Couldn't even get the command to start. Most likely it couldn't be found because of a 075 * typo. 076 */ 077 System.out.println("Error starting: " + args[0]); 078 System.out.println(e); 079 } 080 } 081 082 private StdOut so; 083 084 private StdOut se; 085 086 public String getOutputData() 087 { 088 if (so != null) 089 { 090 return so.getData(); 091 } 092 else 093 { 094 return ""; 095 } 096 } 097 098 public String getErrorData() 099 { 100 if (se != null) 101 { 102 return se.getData(); 103 } 104 else 105 { 106 return ""; 107 } 108 } 109 110 public Console(Process p) 111 { 112 JFrame frame = new JFrame(); 113 frame.setTitle("Console"); 114 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 115 frame.setLocation(screenSize.width / 2 - INITIAL_WIDTH / 2, screenSize.height / 2 116 - INITIAL_HEIGHT / 2); 117 ConsoleTextArea cta = new ConsoleTextArea(); 118 JScrollPane scroll = new JScrollPane(cta); 119 scroll.setPreferredSize(new Dimension(INITIAL_WIDTH, INITIAL_HEIGHT)); 120 frame.getContentPane().add(scroll); 121 frame.pack(); 122 123 // From here down your shell should be pretty much 124 // as it is written here! 125 /* 126 * Start up StdOut, StdIn and StdErr threads that write the output generated by the process 127 * p to the screen, and feed the keyboard input into p. 128 */ 129 so = new StdOut(p, cta); 130 se = new StdOut(p, cta); 131 StdIn si = new StdIn(p, cta); 132 so.start(); 133 se.start(); 134 si.start(); 135 136 // Wait for the process p to complete. 137 try 138 { 139 frame.setVisible(true); 140 p.waitFor(); 141 } 142 catch (InterruptedException e) 143 { 144 /* 145 * Something bad happened while the command was executing. 146 */ 147 System.out.println("Error during execution"); 148 System.out.println(e); 149 } 150 151 /* 152 * Now signal the StdOut, StdErr and StdIn threads that the process is done, and wait for 153 * them to complete. 154 */ 155 try 156 { 157 so.done(); 158 se.done(); 159 si.done(); 160 so.join(); 161 se.join(); 162 si.join(); 163 } 164 catch (InterruptedException e) 165 { 166 // Something bad happend to one of the Std threads. 167 System.out.println("Error in StdOut, StdErr or StdIn."); 168 System.out.println(e); 169 } 170 frame.setVisible(false); 171 } 172} 173 174class StdIn extends Thread 175{ 176 177 private BufferedReader kb; 178 179 private boolean processRunning; 180 181 private PrintWriter op; 182 183 public StdIn(Process p, ConsoleTextArea cta) 184 { 185 setDaemon(true); 186 InputStreamReader ir = new InputStreamReader(cta.getIn()); 187 kb = new BufferedReader(ir); 188 189 BufferedOutputStream os = new BufferedOutputStream(p.getOutputStream()); 190 op = new PrintWriter((new OutputStreamWriter(os)), true); 191 processRunning = true; 192 } 193 194 public void run() 195 { 196 try 197 { 198 while (kb.ready() || processRunning) 199 { 200 if (kb.ready()) 201 { 202 op.println(kb.readLine()); 203 } 204 } 205 } 206 catch (IOException e) 207 { 208 System.err.println("Problem reading standard input."); 209 System.err.println(e); 210 } 211 } 212 213 public void done() 214 { 215 processRunning = false; 216 } 217} 218 219class StdOut extends Thread 220{ 221 222 private InputStreamReader output; 223 224 private boolean processRunning; 225 226 private ConsoleTextArea cta; 227 228 private StringBuffer data; 229 230 public StdOut(Process p, ConsoleTextArea cta) 231 { 232 setDaemon(true); 233 output = new InputStreamReader(p.getInputStream()); 234 this.cta = cta; 235 processRunning = true; 236 data = new StringBuffer(); 237 } 238 239 public void run() 240 { 241 try 242 { 243 /* 244 * Loop as long as there is output from the process to be displayed or as long as the 245 * process is still running even if there is presently no output. 246 */ 247 while (output.ready() || processRunning) 248 { 249 250 // If there is output get it and display it. 251 if (output.ready()) 252 { 253 char[] array = new char[255]; 254 int num = output.read(array); 255 if (num != -1) 256 { 257 String s = new String(array, 0, num); 258 data.append(s); 259 SwingUtilities.invokeAndWait(new ConsoleWrite(cta, s)); 260 } 261 } 262 } 263 } 264 catch (Exception e) 265 { 266 System.err.println("Problem writing to standard output."); 267 System.err.println(e); 268 } 269 } 270 271 public void done() 272 { 273 processRunning = false; 274 } 275 276 public String getData() 277 { 278 return data.toString(); 279 } 280} 281 282class ConsoleWrite implements Runnable 283{ 284 285 private ConsoleTextArea textArea; 286 287 private String str; 288 289 public ConsoleWrite(ConsoleTextArea textArea, String str) 290 { 291 this.textArea = textArea; 292 this.str = str; 293 } 294 295 public void run() 296 { 297 textArea.write(str); 298 } 299}; 300 301class ConsoleWriter extends java.io.OutputStream 302{ 303 304 private ConsoleTextArea textArea; 305 306 private StringBuffer buffer; 307 308 public ConsoleWriter(ConsoleTextArea textArea) 309 { 310 this.textArea = textArea; 311 buffer = new StringBuffer(); 312 } 313 314 public synchronized void write(int ch) 315 { 316 buffer.append((char) ch); 317 if (ch == '\n') 318 { 319 flushBuffer(); 320 } 321 } 322 323 public synchronized void write(char[] data, int off, int len) 324 { 325 for (int i = off; i < len; i++) 326 { 327 buffer.append(data[i]); 328 if (data[i] == '\n') 329 { 330 flushBuffer(); 331 } 332 } 333 } 334 335 public synchronized void flush() 336 { 337 if (buffer.length() > 0) 338 { 339 flushBuffer(); 340 } 341 } 342 343 public void close() 344 { 345 flush(); 346 } 347 348 private void flushBuffer() 349 { 350 String str = buffer.toString(); 351 buffer.setLength(0); 352 SwingUtilities.invokeLater(new ConsoleWrite(textArea, str)); 353 } 354}; 355 356class ConsoleTextArea extends JTextArea implements KeyListener, DocumentListener 357{ 358 359 /** 360 * 361 */ 362 private static final long serialVersionUID = 3258410625414475827L; 363 364 private ConsoleWriter console1; 365 366 private ConsoleWriter console2; 367 368 private PrintStream out; 369 370 private PrintStream err; 371 372 private PrintWriter inPipe; 373 374 private PipedInputStream in; 375 376 private java.util.Vector history; 377 378 private int historyIndex = -1; 379 380 private int outputMark = 0; 381 382 public void select(int start, int end) 383 { 384 requestFocus(); 385 super.select(start, end); 386 } 387 388 public ConsoleTextArea() 389 { 390 super(); 391 history = new java.util.Vector(); 392 console1 = new ConsoleWriter(this); 393 console2 = new ConsoleWriter(this); 394 out = new PrintStream(console1); 395 err = new PrintStream(console2); 396 PipedOutputStream outPipe = new PipedOutputStream(); 397 inPipe = new PrintWriter(outPipe); 398 in = new PipedInputStream(); 399 try 400 { 401 outPipe.connect(in); 402 } 403 catch (IOException exc) 404 { 405 exc.printStackTrace(); 406 } 407 getDocument().addDocumentListener(this); 408 addKeyListener(this); 409 setLineWrap(true); 410 setFont(new Font("Monospaced", 0, 12)); 411 } 412 413 void returnPressed() 414 { 415 Document doc = getDocument(); 416 int len = doc.getLength(); 417 Segment segment = new Segment(); 418 try 419 { 420 synchronized (doc) 421 { 422 doc.getText(outputMark, len - outputMark, segment); 423 } 424 } 425 catch (javax.swing.text.BadLocationException ignored) 426 { 427 ignored.printStackTrace(); 428 } 429 if (segment.count > 0) 430 { 431 history.addElement(segment.toString()); 432 } 433 historyIndex = history.size(); 434 inPipe.write(segment.array, segment.offset, segment.count); 435 append("\n"); 436 synchronized (doc) 437 { 438 outputMark = doc.getLength(); 439 } 440 inPipe.write("\n"); 441 inPipe.flush(); 442 console1.flush(); 443 } 444 445 public void eval(String str) 446 { 447 inPipe.write(str); 448 inPipe.write("\n"); 449 inPipe.flush(); 450 console1.flush(); 451 } 452 453 public void keyPressed(KeyEvent e) 454 { 455 int code = e.getKeyCode(); 456 if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_LEFT) 457 { 458 if (outputMark == getCaretPosition()) 459 { 460 e.consume(); 461 } 462 } 463 else if (code == KeyEvent.VK_HOME) 464 { 465 int caretPos = getCaretPosition(); 466 if (caretPos == outputMark) 467 { 468 e.consume(); 469 } 470 else if (caretPos > outputMark) 471 { 472 if (!e.isControlDown()) 473 { 474 if (e.isShiftDown()) 475 { 476 moveCaretPosition(outputMark); 477 } 478 else 479 { 480 setCaretPosition(outputMark); 481 } 482 e.consume(); 483 } 484 } 485 } 486 else if (code == KeyEvent.VK_ENTER) 487 { 488 returnPressed(); 489 e.consume(); 490 } 491 else if (code == KeyEvent.VK_UP) 492 { 493 historyIndex--; 494 if (historyIndex >= 0) 495 { 496 if (historyIndex >= history.size()) 497 { 498 historyIndex = history.size() - 1; 499 } 500 if (historyIndex >= 0) 501 { 502 String str = (String) history.elementAt(historyIndex); 503 int len = getDocument().getLength(); 504 replaceRange(str, outputMark, len); 505 int caretPos = outputMark + str.length(); 506 select(caretPos, caretPos); 507 } 508 else 509 { 510 historyIndex++; 511 } 512 } 513 else 514 { 515 historyIndex++; 516 } 517 e.consume(); 518 } 519 else if (code == KeyEvent.VK_DOWN) 520 { 521 int caretPos = outputMark; 522 if (history.size() > 0) 523 { 524 historyIndex++; 525 if (historyIndex < 0) 526 { 527 historyIndex = 0; 528 } 529 int len = getDocument().getLength(); 530 if (historyIndex < history.size()) 531 { 532 String str = (String) history.elementAt(historyIndex); 533 replaceRange(str, outputMark, len); 534 caretPos = outputMark + str.length(); 535 } 536 else 537 { 538 historyIndex = history.size(); 539 replaceRange("", outputMark, len); 540 } 541 } 542 select(caretPos, caretPos); 543 e.consume(); 544 } 545 } 546 547 public void keyTyped(KeyEvent e) 548 { 549 int keyChar = e.getKeyChar(); 550 if (keyChar == 0x8 /* KeyEvent.VK_BACK_SPACE */) 551 { 552 if (outputMark == getCaretPosition()) 553 { 554 e.consume(); 555 } 556 } 557 else if (getCaretPosition() < outputMark) 558 { 559 setCaretPosition(outputMark); 560 } 561 } 562 563 public void keyReleased(KeyEvent e) 564 { 565 } 566 567 public synchronized void write(String str) 568 { 569 insert(str, outputMark); 570 int len = str.length(); 571 outputMark += len; 572 select(outputMark, outputMark); 573 } 574 575 public synchronized void insertUpdate(DocumentEvent e) 576 { 577 int len = e.getLength(); 578 int off = e.getOffset(); 579 if (outputMark > off) 580 { 581 outputMark += len; 582 } 583 } 584 585 public synchronized void removeUpdate(DocumentEvent e) 586 { 587 int len = e.getLength(); 588 int off = e.getOffset(); 589 if (outputMark > off) 590 { 591 if (outputMark >= off + len) 592 { 593 outputMark -= len; 594 } 595 else 596 { 597 outputMark = off; 598 } 599 } 600 } 601 602 public void postUpdateUI() 603 { 604 // this attempts to cleanup the damage done by updateComponentTreeUI 605 requestFocus(); 606 setCaret(getCaret()); 607 synchronized (this) 608 { 609 select(outputMark, outputMark); 610 } 611 } 612 613 public void changedUpdate(DocumentEvent e) 614 { 615 } 616 617 public InputStream getIn() 618 { 619 return in; 620 } 621 622 public PrintStream getOut() 623 { 624 return out; 625 } 626 627 public PrintStream getErr() 628 { 629 return err; 630 } 631 632};