[Java] Daemon Console Reader

Introduction

So, I was recently Team programming with one of my clan mates on a project for stat collection he was working on. He wanted help with class structure and how to organize his code. So we both booted up eclipse and using Saros (Paired Programming over a network; I highly recommend you check it out!) we got to work. After several hours and some intense programming we came up with the skeleton of the program. We had the Driver, the actual Daemon, and the worker threads. We were about finished when it occurred to me, “How do we gracefully shut down this puppy?” It has no GUI and killing from the task manager isn’t very graceful. The answer was to read input from the console and act upon it. Thus I present to you my adventures in creating a console reader!

The Console Scanner

So the first problem I encountered was, “How do I read input from the console?” The answer is easy enough in that you can use System.in provided by Java and wrapped by an InputStreamReader and further wrapped by a BufferedReader. This allows a programmer to read input from the Console by using the readLine() method in BufferedReader. Outputting messages and receiving the input while ensuring that the main daemon executes can be solved by putting the Console Reader in it’s own thread. So we implement Runnable and just continuously loop reading input. Then after each input is received, we process it for commands and respond. Here is what the ConsoleReader Code looks like.

package testing.ConsoleReader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ConsoleReader implements Runnable
{
	private boolean bExit = false;
	
	private boolean bPause = false;
	
	private BufferedReader br = new BufferedReader(new InputStreamReader(
			System.in));
	
	private String sInput;
	
	public boolean isExit()
	{
		return bExit;
	}
	
	public boolean isPaused()
	{
		return bPause;
	}
	
	@Override
	public void run()
	{
		printTitle();
		
		do
		{
			try
			{
				sInput = br.readLine();
				
				sInput = sInput.toLowerCase();
			}
			catch (IOException e)
			{
				System.out.println("IO error trying to read your command!");
			}
			
			if (sInput.equals("quit"))
			{
				bExit = true;
				
				System.out.println("Goodbye!");
			}
			else if (sInput.equals("pause"))
			{
				if (!bPause)
				{
					bPause = true;
					
					System.out.println("Threads paused...");
				}
				else if (bPause)
				{
					System.out
							.println("Threads already paused! Type 'resume' "
									+ "to resume thread execution.");
				}
				
			}
			else if (sInput.equals("resume"))
			{
				if (!bPause)
				{
					System.out.println("Threads not paused! Type 'pause' "
							+ "to pause thread execution.");
				}
				else if (bPause)
				{
					bPause = false;
					System.out.println("Thread execution resumed...");
				}
			}
			else if (sInput.equals("help"))
			{
				printHelp();
			}
			else
			{
				System.out.println("'" + sInput + "' not recognized as a "
						+ "valid command.");
				
				printHelp();
			}
			
		} while (!bExit);
	}
	
	private void printHelp()
	{
		System.out.println("Type 'quit' to Exit the application.\n"
				+ "Type 'pause' to pause thread execution.\n"
				+ "Type 'resume' to resume thread execution.\n"
				+ "Type 'help' for help.");
	}
	
	private void printTitle()
	{
		System.out.println("********** Daemon **********\n"
				+ "Type 'help' and press ENTER for commands.");
	}
}

As you can see I implement the run() method of the Runnable Interface. Inside I first print a Title, and then enter a DO-WHILE loop to constantly check for input and respond to it. I push the input to lowercase to ensure there are no mistakes in reading what the user inputs. I have several booleans with getters and setters for use later in the daemon class.

Now my Daemon Class is very simple as it’s just an example, but you should be able to get a grip on what I’m doing with the ConsoleReader’s booleans.

 

package testing.ConsoleReader;

import javax.swing.JOptionPane;

public class Daemon extends Thread
{
	private ConsoleReader cr;
	
	public Daemon(ConsoleReader reader)
	{
		cr = reader;
	}
	
	@Override
	public void run()
	{
		while (!cr.isExit())
		{
			if(!cr.isPaused())
			{
				JOptionPane.showMessageDialog(null, "Weeee! We are executing!");
				
				try
				{
					Thread.sleep(1000);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
	}
}

My Daemon extends Thread so I can set it as a daemon and use all the nifty tricks of the Thread class. It’s quit possible I could just implement Runnable but… this is the way i went about it. My Daemon class has a constructor to take a ConsoleReader so it can check on the boolean values we saw earlier. There could be another constructor that takes nothing so if you wanted to run the Daemon without a ConsoleReader you could. Next, I override the run() method of Thread and add my while loop. The while loop checks to see if it should continue execution based upon feedback from the console. Then we check inside of the WhileLoop to see if we are paused. If we are paused we would probably tell the worker threads to stop() or prevent creation of any more worker threads. I use a JOptionPane to simulate a worker thread executing and being monitored by the Daemon Thread. I then sleep so we aren’t spammed by the JOptionPane.

Lastly we need to combine everything in the Driver.

 

package testing.ConsoleReader;

public class Driver
{
	public static void main(String[] args)
	{
		ConsoleReader cr = new ConsoleReader();
		Thread t = new Thread(cr);
		t.start();
		
		Daemon d = new Daemon(cr);
		d.setDaemon(true);
		d.start();
	}
}

Here we create a new ConsoleReader and add it to a Thread, and tell that Thread to start so we can listen for commands. Next we create a new Daemon, make sure that the JVM knows it’s a daemon, and we start it. It’s as simple as that! Now what we do is we run the above from the command line and watch as the the JOptionPane continues to pop up as the Daemon keeps calling it. Then in the console we can pause, resume, and exit the process at our leisure! This is also where, if we wanted, we could change the Daemon to start without a console reader simply by reading in the command line arguments. An Example like so:

 

package testing.ConsoleReader;

public class Driver
{
	public static void main(String[] args)
	{
		ConsoleReader cr;
		Daemon d;
		
		if(args.length > 0)
		{
			if (args[0].toUpperCase().equals("-NOCONSOLE"))
			{
				d = new Daemon();
				d.setDaemon(true);
				d.start();
			}
			else
			{
				cr = new ConsoleReader();
				Thread t = new Thread(cr);
				t.start();
				
				d = new Daemon(cr);
				d.setDaemon(true);
				d.start();
			}
		}
		else
		{
			cr = new ConsoleReader();
			Thread t = new Thread(cr);
			t.start();
			
			d = new Daemon(cr);
			d.setDaemon(true);
			d.start();
		}
	}
}

Anyways, I hope this little article has been informative. You can download the SourceCode from here and try it yourself. Just compile and run from the command line!