Test and Debug

Design by tests. When you have the goal in mind you built a test case. Every functional piece should be able to be tested in isolation.

For instance, validating a login, listing an article, and updating a row in the database all should be testable separate from the whole application.

Accomplishing this goal is helped by using common utilities such as:
– command line parser
– logging
– web browser debugger

Main interfaces will be in most every class. HTML files can be opened on the file system. Firebug in Firefox. Log4j and Java Command Line Option (JCLO) libraries are crucial elements of a successful test driven development strategy. Even JQuery has a log function.

For example, we can create a Log class to encapsulate log4j that will allow us to run without log4j, into stdout and stderr. All our classes can call Log.write(…) with various levels of detail, error, warn, info, debug, etc…

Like this:

package com.doncohoon.util;

import java.util.Date;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;

import com.doncohoon.db.Access;

public class Log {
	static boolean v = false;
	public final int all_int    = Level.ALL_INT;
	public final int trace_int  = Level.TRACE_INT;
	public final int debug_int  = Level.DEBUG_INT;
	public final int error_int  = Level.ERROR_INT;
	public final int fatal_int  = Level.FATAL_INT;
	public final int info_int   = Level.INFO_INT;
	public final int off_int    = Level.OFF_INT;
	public final int stdout_int = 1; // emergency standard log
	public final int stderr_int = 2; // emergency error log
	private Logger logger = null;
	
	public Log(Logger L) {
	   this.logger = L;	
	}
	
	public Log(Logger L, boolean debug) {
		if (debug) {
			v = true;
		}
		this.logger = L;		
	}
		
	public void setVerbose (Logger logger) {
		logger.setLevel(Level.DEBUG);
		v = true;
	}
	
	public void all(String msg, Exception e) {
		write (all_int, msg);
		logger.info("Exception: ", e);
	}
	public void write(int level, String msg, Exception e) {
		write (level, msg);
		logger.fatal("Exception: ", e);
	}
	public void write(int level, String msg) {

		// Switch
		switch (level) {
		case Level.ALL_INT:
			logger.info(msg);
			break;
		case Level.TRACE_INT:
			logger.trace(msg);
			break;			
		case Level.DEBUG_INT:
			if (logger.isDebugEnabled())
			    logger.debug(msg);
			else if (v)
				write (stdout_int, msg);
			break;
		case Level.ERROR_INT:
			logger.error(msg);
			break;
		case Level.FATAL_INT:
			logger.fatal(msg);
			break;
		case Level.INFO_INT:
			logger.info(msg);
			break;
		case Level.OFF_INT:
			logger.setLevel(Level.OFF);  
			break;
		case Level.WARN_INT:
			logger.warn(msg);
			break;
		case stdout_int:
			Date now1 = new Date();
			System.out.println(now1.toString() + "-App:" + msg);
			break;
		case stderr_int:
			Date now2 = new Date();
			System.err.println(now2.toString() + "-App:" + msg);
			break;
		default:
			Date now = new Date();
			System.err.println(now.toString() + "-App:Invalid level."
					+ level);
			break;
		} // switch

	} // write
}

The other technique is to be able to call each class from the command line and exercise its methods. For this we will need a command line argument parser for the main(…) method. JCLO works quite well.

Like this, we add a Args class to each class. For class NanoHTTPD we add the following:

 /*
  * Command line arguments
  */
class NanoHTTPDArgs {
	private String confdir = "config/";
	private String log4j_xml = "LocalServer_log4j.xml";
	private String db = "DB.xml";
	private String dbdir = null;
	//
	private boolean v = false;
	private String[] additional;
}

and add a parse method:

	/**
	 * Parse command line inputs
	 */	
	private static void parseArguments(String[] args) {
		try {		
			JCLO jclo = new JCLO(new NanoHTTPDArgs(), args);
	
			// Allow debug to override log4j on the command line
			v = jclo.getBoolean("v");
			
			// Allow override APP directory, w/trailing slash
			confdir = jclo.getString("confdir");
			
			// Allow override of log4j xml configuration file
			log4j_xml = jclo.getString("log4j_xml");
			
			// Allow override DB directory, w/trailing slash
			dbdir = jclo.getString("dbdir");
	
			// Allow different database connections
			// i.e.: Database_derby_client.xml, or Database_oracle_client
			db = jclo.getString("db");
	
		} catch (IllegalArgumentException e) {
			String[] arg2 = { "Debug" };
			log(stderr,"---> App: Unknown parameter <---");
		     for (int i = 0; i < args.length; i = i + 1) { // Test and Loop
		    	 log(stderr,args[i]);  
		       }		    	
			JCLO jclo2 = new JCLO(new NanoHTTPDArgs(), arg2);
			log(stderr,jclo2.usage());
		}		
	} // parseArguments

then finally parse the main input parameters, like this:

	public static void main( String[] args )
	{
		/* parse the arguments */
        parseArguments(args);
.  .  .		
       // call all the normal methods here

      // then print some results to System.out.println(...);

      // and exit with a zero, one, two, etc... so the unit test driver can determine success
       }

If the class is called from command line, main is called, does some processing and returns results via stdout, or if the class is instantiated via another class, the same processing may be done and returned results sent back via some object result set. See? NanoHTTPD.java

Previous

Model View Control

Control ties the web server (view) to our database (model). In this implementation views are further defined using HTML files, Java classes will glue database stored procedure models containing SQL.

Model View Control Thought Process

Example of Model View Control Thought Process

HTML5 files will use JavaScript to call APIs which determine the next step, or control. Models are entirely accessed in stored procedures within the database, via call statements, keeping CReate Update and Delete (CRUD) SQL separate from the decisions.

Security controls are also in the controller while delegating either an LDAP or DB model repository to store usernames and passwords.

These defined roles will allow changes to display formats; laptop vs mobile, or database; Derby vs Oracle, to evolve.

  1. Next

Data Base

Data Base systems are intended to organize, store, and retrieve large amounts of data easily. Whether it is a collections of files, has as SQL language interface, key-value pairs, clustered, or a single file, it should make our lives easier.

Coupled with our Web Server from my last article, we will have the foundation of dynamic shared information over the network of interconnected hosts; our be-loved Web.

Keeping with the theme of portability and Java, JavaDB is a good choice. We want a SQL interface and JDBC because they are well known and the DB engine can be switched out later if desired without core code changes. Produced by the Apache foundation under the name Derby, the latest release can be found here:

Apache Derby, an Apache DB subproject, is an open source relational database implemented entirely in Java and available under the Apache License, Version 2.0. Some key advantages include:

* Derby has a small footprint — about 2.6 megabytes for the base engine and embedded JDBC driver.
* Derby is based on the Java, JDBC, and SQL standards.
* Derby provides an embedded JDBC driver that lets you embed Derby in any Java-based solution.
* Derby also supports the more familiar client/server mode with the Derby Network Client JDBC driver and Derby Network Server.
* Derby is easy to install, deploy, and use.

Download it here: Derby Download

With our database comes the responsibility of a rational file system structure for our code base deployment. I chose the name trutree as my project name, so let us start simple:

Create a workspace:

$ mkdir -p ~/Documents/workspace/trutree
$ cd ~/Documents/workspace/trutree

Copy the derby library:

$ mkdir lib
$ cd lib
$ tar xzvf ~/Downloads/db-derby-10.7.1.1-bin.tar.gz
$ ls 
KEYS                    bin                     javadoc
LICENSE                 demo                    lib
NOTICE                  docs                    test
RELEASE-NOTES.html      index.html

Create an environment setting file (notice the difference between Linux and Mac OS X ‘Darwin’):

$ cd ~/Documents/workspace/trutree
$ vi derby.env
~
if [[ $(uname) == "Linux" ]]; then
  export DERBY_HOME=/home/don/workspace/trutree/lib/db-derby-10.7.1.1-bin
  export TRUTREE_HOME=/home/don/workspace/trutree
elif [[ $(uname) == "Darwin" ]]; then
  export DERBY_HOME=/Users/don/Documents/workspace/trutree/lib/db-derby-10.7.1.1-bin
  export TRUTREE_HOME=/Users/don/Documents/workspace/trutree
fi
export PATH=$PATH:$DERBY_HOME/bin
export CLASSPATH=$DERBY_HOME/lib/derby.jar:$DERBY_HOME/lib/derbyclient.jar:$TRUTREE_HOME/bin:.
~

Now create a Java environment file (I am using Java 1.6):

$ vi java.env
~
if [[ $(uname) == "Linux" ]]; then
  export JAVA_HOME=/usr
elif [[ $(uname) == "Darwin" ]]; then
  export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
fi
~

Finally create a script to start and stop the Derby database server (we will run it in client/server mode to enable multi-user/program access, through port 1527):

$ vi derby.sh
~
. derby.env
. java.env
export PATH=$PATH:$DERBY_HOME/bin
export CLASSPATH=$DERBY_HOME/lib/derby.jar:$DERBY_HOME/lib/derbyclient.jar:.

if [[ $1 = 'start' ]]
then
  $JAVA_HOME/bin/java -jar $DERBY_HOME/lib/derbyrun.jar server start
fi
if [[ $1 = 'stop' ]]
then
  $JAVA_HOME/bin/java -jar $DERBY_HOME/lib/derbyrun.jar server shutdown
fi
~

Verify Derby

Run the sysinfo command, as shown below, to output Derby system information:

java org.apache.derby.tools.sysinfo

Successful output will look something like this:

$ . java.env
$ . derby.env
$ java org.apache.derby.tools.sysinfo
------------------ Java Information ------------------
Java Version:    1.6.0_24
Java Vendor:     Apple Inc.
Java home:       /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Java classpath:  /Users/don/Documents/workspace/trutree/lib/db-derby-10.7.1.1-bin/lib/derby.jar:/Users/don/Documents/workspace/trutree/lib/db-derby-10.7.1.1-bin/lib/derbyclient.jar:/Users/don/Documents/workspace/trutree/bin:.
OS name:         Mac OS X
OS architecture: x86_64
OS version:      10.5.8
Java user name:  don
Java user home:  /Users/don
Java user dir:   /Users/don/Documents/workspace/trutree
java.specification.name: Java Platform API Specification
java.specification.version: 1.6
java.runtime.version: 1.6.0_24-b07-334-9M3326
--------- Derby Information --------
JRE - JDBC: Java SE 6 - JDBC 4.0
[/Users/don/Documents/workspace/trutree/lib/db-derby-10.7.1.1-bin/lib/derby.jar] 10.7.1.1 - (1040133)
[/Users/don/Documents/workspace/trutree/lib/db-derby-10.7.1.1-bin/lib/derbyclient.jar] 10.7.1.1 - (1040133)
------------------------------------------------------
----------------- Locale Information -----------------
Current Locale :  [English/United States [en_US]]
Found support for locale: [cs]
         version: 10.7.1.1 - (1040133)

Now we start the server in a new command window (Use /Applications/Utilities/Terminal on Mac, or Accessories > Terminal on Linux). Make sure you cd to the correct directory first!

$ cd ~/Documents/workspace/trutree
$ derby.sh start
Sun Apr 10 14:58:29 EDT 2011 : Security manager installed using the Basic server security policy.
Sun Apr 10 14:58:30 EDT 2011 : Apache Derby Network Server - 10.7.1.1 - (1040133) started and ready to accept connections on port 1527

Then we create the database using Derby’s command line tool in another Terminal window, ij:

$ cd ~/Documents/workspace/trutree
$ ij 
ij version 10.7
ij> connect 'jdbc:derby://localhost:1527/trutree;user=app;create=true';
ij> exit;

Check for your database files (notice there are in the directory where you started the DB server, i.e.: ~/Documents/workspace/trutree/trutree):

$ ls -l trutree
total 8
drwxrwxrwx   3 don  don   102 Dec 22 05:29 jar
drwxrwxrwx   5 don  don   170 Mar 26 18:25 log
drwxrwxrwx  84 don  don  2856 Dec 22 05:29 seg0
-rwxrwxrwx   1 don  don   870 Nov  6 18:01 service.properties

Good deal! Now we have a database server and a new database.

To browse around the new database I recommend SQL Squirrel.
Download and install the package for your system, it supports Linux, Mac OS X, and Windows.
Configure the driver for Derby, then connect to your new database:

Alias: trutree
Driver: Apache Derby Client
URL: jdbc:derby://localhost:1527/trutree
User: app
Password: app

If all is good, here is what you will see:

SQL SQuirreL

trutree database initial connection using SQL Squirrel

Next