Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Java Console Apps Made Easy

4.83/5 (20 votes)
18 Mar 2018CPOL5 min read 135.7K   7.1K  
This article describes the creation of a Java console type application.

Demo screenshot

Contents

Introduction

The humble console app: I write them all the time creating one off tests to explore new coding ideas. I even string up quick, menu based, test applications for testing remote device communications and command sets, where the number of commands can grow during the product development cycle. The advantage of course, is that to grow the application I only have to add a menu item and an event handler in the code. That is far preferable to squeezing more fields onto an already crowded Windows Forms application. Sometimes you don't know what you have till it's gone.

I decided to tackle Java this year and get a feel for the language and tools. I usually take on a new (for me) language by translating an application I have written into the new language and stumbling through the thorny issues as I encounter them. I was surprised at how many of the things I took for granted in C# turned out to be rather hard to do in Java; case in point, the console app.

The application I chose to port to Java happened to be a console based menu driven application. Porting the menu code was fairly straightforward once I figured out how to substitute an interface and nested Java class for a C# delegate in order to obtain callback behavior. The real headache lay in the performance of the application's menu code in an actual Windows console instance.

Java applications utilize STDIN, STDOUT, and STDERR streams but do not own the console instance displaying the streams. Since Java aims at a broad range of Operating Systems, it doesn't include support for clearing the screen. I tried a number of work-a-rounds to get my application to function as intended without acceptable results.

I was about to give up when I took a different track and considered duplicating console functionality in a Java GUI widget. This turned out to be more promising. There were quite a few examples of redirecting STDOUT and STDERR to a JtextArea but I was unable to find one that also successfully copied keyboard input to STDIN. What I did find was quite a bit of chatter from other developers indicating a desire for such a component.

I began to test the various code solutions and snippets that I had found and settled on one particularly robust example[^]. I then added some code to pipe keyboard characters to STDIN, added a public clear() method, and some properties to customize the look and feel of the console. Finally, I figured out how to create a custom blinking block cursor for JavaConsole to round things off.

How to Use this Java Console

Minimally, all that is necessary to use this Java Console in your project is to declare an instance of the JavaConsole class.

Java
public static void main(String[] args) {

    new JavaConsole();
    scanner = new Scanner(System.in);

    System.out.println("Hello World!");
    scanner.nextLine();
    
    System.exit(0);
}

Here is an example of how to customize the look of the Java Console.

Java
public static void main(String[] args){
    
    Scanner scanner = new Scanner(System.in);
    JavaConsole console = new JavaConsole();
    
    //Customize the console text field
    console.setBackground(Color.WHITE);
    console.setForeground(Color.BLACK);
    console.setFont(new Font ("Ariel", Font.BOLD, 12));
    
    //Customize the console frame
    console.setTitle("Hello World Program");
    
    Image im = Toolkit.getDefaultToolkit().getImage("../images/MyIcon.png");
    console.setIconImage(im);
    
    System.out.println("Hello World!");
    scanner.nextLine();
    
    System.exit(0);
}

Here is an example using the JavaConsole.clear() method to clear the screen between chunks of text.

Java
public static void main(String[] args){
    
    Scanner scanner = new Scanner(System.in);
    JavaConsole console = new JavaConsole();

    System.out.println(
            "It was a dark and stormy night; the rain fell in torrents, except at " +
            "occasional intervals, when it was checked by a violent gust of wind " +
            "which swept up the streets (for it is in London that our scene lies), " +
            "rattling along the house-tops, and fiercely agitating the scanty flame " +
            "of the lamps that struggled against the darkness. Through one of the " +
            "obscurest quarters of London, and among haunts little loved by the " +
            "gentlemen of the police, a man, evidently of the lowest orders, was " +
            "wending his solitary way.");
    
    scanner.nextLine();
    console.clear();
    System.out.println(
            "He stopped twice or thrice at different shops and houses of a description " +
            "correspondent with the appearance of the quartier in which they were situated, " +
            "and tended inquiry for some article or another which did not seem easily " +
            "to be met with. All the answers he received were couched in the negative; " +
            "and as he turned from each door he muttered to himself, in no very elegant " +
            "phraseology, his disappointment and discontent. At length, at one house, " +
            "the landlord, a sturdy butcher, after rendering the same reply the inquirer " +
            "had hitherto received, added, \"But if this vill do as vell, Dummie, it is " +
            "quite at your sarvice!\"");
    
    scanner.nextLine();
    
    System.exit(0);
}

Finally, here's a comprehensive list of Java Console custom features.

Java
console.getBackground();
console.getForeground();
console.getFont();

console.setBackground(Color);
console.setForeground(Color);
console.setFont(Font);

console.setTitle(title);
console.setIconImage(Image);

console.clear();

How to Wire Up a Console

The first thing we need to do is to redirect STDOUT and STDERR to our JtextArea. The code class I started with achieved this in the following manner:

Java
private final PipedInputStream pin=new PipedInputStream(); 
private final PipedInputStream pin2=new PipedInputStream();
Java
PipedOutputStream pout=new PipedOutputStream(this.pin);
PipedOutputStream pout2=new PipedOutputStream(this.pin2);
Java
System.setOut(new PrintStream(pout,true)); 
System.setErr(new PrintStream(pout2,true));
Java
//Declarations
private Thread reader;
private Thread reader2;

//In the class constructor
reader=new Thread(this);
reader.setDaemon(true);
reader.start();

reader2=new Thread(this);
reader2.setDaemon(true);
reader2.start();
Java
public synchronized void run()
{
    try
    {           
        while (Thread.currentThread()==reader)
        {
            try { this.wait(100);}catch(InterruptedException ie) {}
            if (pin.available()!=0)
            {
                String input=this.readLine(pin);
                textArea.append(input);
                textArea.setCaretPosition(textArea.getDocument().getLength()); //DWM 02-07-2012
            }
            if (quit) return;
        }
    
        while (Thread.currentThread()==reader2)
        {
            try { this.wait(100);}catch(InterruptedException ie) {}
            if (pin2.available()!=0)
            {
                String input=this.readLine(pin2);
                textArea.append(input);
                textArea.setCaretPosition(textArea.getDocument().getLength()); //DWM 02-07-2012
            }
            if (quit) return;
        }           
    } catch (Exception e)
    {
        textArea.append("\nConsole reports an Internal error.");
        textArea.append("The error is: "+e);
        textArea.setCaretPosition(textArea.getDocument().getLength()); //DWM 02-07-2012
    }
    
}
  1. Establish some Piped Input Streams.
  2. Hook the input streams up to the associated output streams.
  3. Redirect System STDOUT and STDERR to these output streams.
  4. Spin up some threads to read from the Piped Input Streams.
  5. Monitor STDOUT and STDERR appending the text to the JtextArea.

Now that we have STDOUT and STDERR piped to the JtextArea, we need to monitor keyboard input to the JtextArea and copy the characters to STDIN. I modified the original code to do so as follows:

Java
private final PipedOutputStream pout3=new PipedOutputStream();
Java
System.setIn(new PipedInputStream(this.pout3));
Java
textArea.addKeyListener(new KeyListener() {

    public void keyPressed(KeyEvent e) {}

    public void keyReleased(KeyEvent e) {}

    public void keyTyped(KeyEvent e)  {
            try { pout3.write(e.getKeyChar()); } catch (IOException ex) {}
    }});
  1. Establish a Piped Output Stream for reading keyboard input.
  2. Hook the output stream to an associated input stream and redirect STDIN to this input stream.
  3. Add a KeyListener and copy out keyboard input as it arrives.

Making a Custom Block Caret

The default caret of the JtextArea is a vertical bar but I wanted a prompt that was a bit more prominent. It turns out that this feature is customizable and I found some examples on how to do this. Refer to A custom caret class [^] and Fancier custom caret class [^] to get an idea of how it's done. Here is the code for my flashing block caret:

Java
public class BlockCaret extends DefaultCaret {

    private static final long serialVersionUID = 1L;

    /**
     * @brief Class Constructor
     */
    public BlockCaret() {
        setBlinkRate(500); // half a second
    }

    /* (non-Javadoc)
     * @see javax.swing.text.DefaultCaret#damage(java.awt.Rectangle)
     */
    protected synchronized void damage(Rectangle r) {
        if (r == null)
            return;

        // give values to x,y,width,height (inherited from java.awt.Rectangle)
        x = r.x;
        y = r.y;
        height = r.height;
        // A value for width was probably set by paint(), which we leave alone.
        // But the first call to damage() precedes the first call to paint(), so
        // in this case we must be prepared to set a valid width, or else
        // paint()
        // will receive a bogus clip area and caret will not get drawn properly.
        if (width <= 0)
            width = getComponent().getWidth();
        
        repaint();  //Calls getComponent().repaint(x, y, width, height) to erase 
        repaint();  // previous location of caret. Sometimes one call isn't enough.
    }

    /* (non-Javadoc)
     * @see javax.swing.text.DefaultCaret#paint(java.awt.Graphics)
     */
    public void paint(Graphics g) {
        JTextComponent comp = getComponent();

        if (comp == null)
            return;

        int dot = getDot();
        Rectangle r = null;
        char dotChar;
        try {
            r = comp.modelToView(dot);
            if (r == null)
                return;
            dotChar = comp.getText(dot, 1).charAt(0);
        } catch (BadLocationException e) {
            return;
        }

        if(Character.isWhitespace(dotChar)) dotChar = '_';

        if ((x != r.x) || (y != r.y)) {
            // paint() has been called directly, without a previous call to
            // damage(), so do some cleanup. (This happens, for example, when
            // the text component is resized.)
            damage(r);
            return;
        }

        g.setColor(comp.getCaretColor());
        g.setXORMode(comp.getBackground()); // do this to draw in XOR mode

        width = g.getFontMetrics().charWidth(dotChar);
        if (isVisible())
            g.fillRect(r.x, r.y, width, r.height);
    }
}

Extra! Extra! The Menu System

I have included the menu system, that I like to use so much, in this demo. It is a Java port of the easy to use console menu featured in this CodeProject article [^]. As I mentioned in the Introduction, working with callbacks in Java is a bit different than C# and the Menu class illustrates this well. Here's how it's done in C#.

C#
delegate void MenuCallback();
C#
private static void HandleItem1
{
     Console.WriteLine("You chose item 1.\n");
     Console.Read();
}

private static void HandleItem2
{
     Console.WriteLine("You chose item 2.\n");
     Console.Read();
}
Java
menu = new Menu();
menu.Add("Item 1", new MenuCallback(HandleItem1));
menu.Add("Item 2", new MenuCallback(HandleItem2));
menu.Show();
  1. I declare a delegate (a function pointer)
  2. Next, I create some event handlers for menu picks
  3. Then, I add them to the Menu like so

Java does not allow for the passing of function pointers and thus delegates. Instead, this behavior is restricted to typed object pointers and thus the class interface becomes the Java equivalent of the delegate. Here's how it's done in Java.

Java
public interface MenuCallback extends EventListener {
    public void Invoke(); 
}
Java
private static void HandleItem1 {
     System.out.println("You chose item 1.\n.");
     scanner.nextLine();
}

private static void HandleItem2 {
     System.out.println("You chose item 2.\n.");
     scanner.nextLine();
}
Java
Menu menu = new Menu(console);
menu.add("Item 1", new MenuCallback() { public void Invoke() {
     HandleItem1(); } });
menu.add("Item 2", new MenuCallback() { public void Invoke() {
     HandleItem2(); } });
menu.show();
  1. I declare an interface (a typed object pointer)
  2. Next, I create some event handlers for menu picks
  3. Then I add them to the Menu like so

It's a bit more verbose since we are in effect, having to wrap our function pointer in a class.

Final Comments

The menu system does not work well in the standard console when used in a Java application. The version of Menu class included here should only be used in this Java Console. This Java Console may be used anytime you want to have a console window that is owned by your application. While it features a clear() method for clearing the screen, it does not, however handle DOS commands or provide a command prompt.

History

  • Feb 09, 2012: Version 1.0.0.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)