October 2005 Archives

Robert Cooper

AddThis Social Bookmark Button

Via Calvin, TestGen4Web is a new SpikeSource web testing tool. It records web interactions, saves an XML file and then lets you run them as a JUnit test (tell me if you have heard this one before). The twist is, the recorder is a Firefox extension.

Ok, Calvin points out one great point: if you are trying to support someone remotely on a bug, you can get them to record their XML log and send it to you. Thats cool. However…

Web Applications are more than just GET/POST these days. By the time you factor in AJAX, maybe Flash(/Laszlo/Flex) there are a lot more parts to doing a regression test than just the GETs and POSTs.

What I don’t understand here, is why we got so far as a Firefox extension that records the activities without getting Firefox as the test engine. It would seem a clever XUL hacker could record JavaScript events and variable values, DOM states and HTTP requests, and give you a test that really (and I mean it this time) works like a browser, because, it is one.

Robert Cooper

AddThis Social Bookmark Button

Related link: http://www.theserverside.com/news/thread.tss?thread_id=37227

Apache MINA is out. This is a multi-protocol server framework that includes a lot of neat stuff. Most notably it is used as the core for ApacheDS which I have written about before. As someone who has gotten… snookered, as it were… into writing some low level protocol type stuff, this is easy for me to appreciate.

At the same time, I have to wonder: What ever happened to “Servlet”? Not HttpServlet or JSP or Struts, but the original idea of the servlet that was supposed to be a protocol independent network processor? I had this exact same question, actually, when Apache gave us the Mailet API as well. It really seems like the framework proliferation is getting more and more out of hand. And yes, right tool for the right job and all that, but having some good 80/20 frameworks coming from the standards people (Sun, IBM?) that everyone can easily understand is important to the language.

Steven Douglas Olson

AddThis Social Bookmark Button

Do you remember a time when a log book was left by a server and you wrote down everything significant that happened?

I know, that is what the electronic logs were for and this technique is “obsolete”. Yet I remember doing this on our test servers when I worked at Novell. The log file on the machine can’t write down things like: “Replaced the NIC card with an NE2000 after repeated system lockup”.

Now, years later, I find myself journalling the day’s activities. Things like: “Wrote a test for the new caching system. The test fails, and part of the system is implemented, need to finish.”

I know it sounds hokey, but it is a tool that helps me drop back into the programming context faster. Most of us have a life outside of programming and the context switch can be easier if we can resurrect the stack. I am only trying to persist my state in my programming context.

Yeah, I know that many would say, just look at the last files you touched, or checked into the source control. But I like the Lewis and Clark feel of having my personal guidebook. I am charting the unexplored territory of new code and I can get lost with all the twists and turns.

So I keep a log book, journal or diary. Whatever you want to call it, a map of where I’ve been in the code and what I want to do next. I do write it with a modern word processor, Open Office Writer to be exact, but the comments are terse and unpretentious like the comments in the old log book kept by the server.

I don’t know if this is a best practice, but I feel that it is a good practice. If you are feeling like it takes you too long to get context on a Monday morning, consider journalling. I hope you find it as useful as I do.

Amir Shevat

AddThis Social Bookmark Button

My team and I are developing open source infrastructure solutions such as MantaRay, our open source JMS provider (yes, we are getting paid to write open source). One of my ongoing tasks is performance improvements. When you develop infrastructure you really need to think performance at all times.

Lately I was wondering if utilizing the new features of java 5.0 (AKA java 1.5) such as generics would improve my code’s performance. Generics looks like a type-safe solution, the theory behind it being that with generics you have an ArrayList that holds Integers or Strings and not an array of objects that needs to be cast to the specific concrete class. If you have the type information of the Object you are working with, then you do not need type checking, virtual method calls or and other operations that are required when using abstract objects.

Using java 1.4.2 I started to check this theory. I created two arrays, one consisting of Objects and the other of MyTestObject objects. I filled the two arrays with MyTestObject objects and then called the toString method on every element in the array. In order to make this clear, here is a small piece of the code:


// use object array
Object[] objectArray = new Object[1000];

for (int i = 0; i < objectArray.length; i++) {
    objectArray[i] = new MyTestObject(i);
}

for (int i = 0; i < objectArray.length; i++) {
    String result = objectArray[i].toString();
}

// use MyTestObject array
MyTestObject[] objectArray = new MyTestObject[1000];

for (int i = 0; i < objectArray.length; i++) {
    objectArray[i] = new MyTestObject(i);
}

for (int i = 0; i < objectArray.length; i++) {
    String  result = objectArray[i].toString();
}

There was a 10% improvement in performance using the MyTestObject-specific array. 10% improvement is a lot for me, so I verified the result by running the test thousands of times and got the same results. If generics are all they are cut to be then they will yield the same performance improvements.

Well, actually that is not the case; I have compiled this simple piece of code in java 5.0:


LinkedList<MyTestObject> list =
    new LinkedList<MyTestObject>();

list.add(new MyTestObject());
MyTestObject result = list.get(0);

Then I decompiled the code and got these results:


LinkedList list = new LinkedList();
list.add(new MyTestObject());
MyTestObject result =
    (MyTestObject)list.get(0);

A mystery! All of the type information has vanished. Later I found the answer on SUN’s site:
“Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler…”

To make sure, I implemented my own LinkedList based on java 1.4.2 LinkedList; I just modified the internals to work with MyTestObject instead of object. My linked list showed better performance then java 5.0 LinkedList, although it was less then 10% it was still better, and I only provided runtime type information.

My conclusion is that the new generics feature is a step in the right direction, it looks like the template of C++ and provides type checking at compile time, it might even be more readable and easier to understand, but it fails to follow through and provide the type information needed at runtime to improve performance. The guys at Sun say that type erasure is needed to ensure backward compatibility. While backward compatibility is an important thing; improving the runtime performance is important too. I guess we need to find some way both can coexist.

Are Generics only syntactical sugaring?

Robert Cooper

AddThis Social Bookmark Button

Related link: http://mevenide.codehaus.org/mevenide-netbeans-project/features.html

So recently I have been tasked with moving and facilitating a migration to Maven for my employer’s Java codebase.

I must admit, I was skeptical of Maven when I first got started. Ant works great, and it is infinitely flexible. However, Maven’s standardization has a lot of selling points. If nothing else, the standardization of “target” names (in the Maven world they are “goals”) helps a lot. While Maven, though being an ASF project past the 1.0 mark, has the code-whiff of something that isn’t quite… finished… I have really become a fan.

The second aspect to this post is my affinity for NetBeans. I know this leaves me in a tiny minority of Java developers, but especially after the 4.0 mark, I have consistently been impressed by what NB offers, and all in one easy package — no getting the “Base” and a thousand plugins, including MyJ2EE which costs (albeit a tiny amount), to get started. One thing, though, that was part of the big NB 4.0 “features” list was the “Ant Based Build System”. It is actually really great, and offers all the things you would expect from an IDE while making it easy to extend the build process in the IDE with custom tasks. However, the Ant scripts NB generated were completely worthless outside of a machine with NB installed. That means, whether you are working in a corporate team with the Odd Man Out IDE or working on open source projects, you lost almost all the great features.

Enter Mevenide. This is a plugin for NetBeans that basically replaces the entire Ant layer with Maven. The best part is, however, it does nothing “special”. Maven will still build your projects perfectly. Even better, the Meveide deps management is WAY better than the one NB comes with. For instance (and this is new to the latest Mevenide rev):

image

Dependencies become a tree on the project view. Mevenide will inspect remote repos and show you the available versions. Updating is as easy as a right-click. You will also notice the little Maven logo thing next to dependencies that are other projects you have open. While it is smart enough to recognize these, it doesn’t yet seems to support cascade “clean-build” cycles from the IDE tool, so you do have to constantly give it the old [type]:install to build deps.

It also completely replaces the project screen with Maven-equivs. The nicer part of which is the project.properties editor. You can see both the current set props for your project:

image

As well as all available properties for a plugin — something that has saved me COUNTLESS trips to maven.apache.org just to find a prop name:

image

The new version also added a quick dropdown to get to all the related project files in the editor, which was a HUGE improvement over having to constantly flip back to the files view to make hand changes:

image

While it does feel you give up a good chunk of the functionality that really sells NetBeans over Eclipse in my mind, Mevenide beats the 80/20 rule hands down. Moreover, using your POM file as a cross-IDE project descriptor makes working and playing well with others a whole lot easier. The Mevenide group has plugins for most big Java IDEs. I have only worked with the NB and Eclipse verions, however. The Eclipse one comes with a nice GUI-lite POM editor (think web.xml editing), but other than that, it doesn’t seem nearly as nice to me. That may simply be because I never seemed to grasp the operational metaphor it is trying to use with a project. Still, though, the Eclipse plugin for Maven on its own gets you 80% of the way there with Eclipse, so they may just not feel there is that much to “do”.

Think I’m a freak for using NetBeans? Got any other good Maven tools?

Bill Siggelkow

AddThis Social Bookmark Button

Related link: http://dbunit.sourceforge.net

If you develop applications that access relational data, you need to verify that your application code works with the data, as designed. When using Hibernate, for example, you should confirm that your mappings direct Hibernate to store and retrieve data correctly. I implement these kind of tests using JUnit. Some testing purists argue that testing against a database belies unit-testing, but no one can refute that testing against a live database is critical — there is no better way to verify your mappings.

But testing against the database requires that the data be in a known initial state. You can load the database manually before you run your test, but that becomes monotonous and error-prone. DBUnit solves this problem. It can pre-load the database for each test based on an XML data set. The data set format is easy to understand; here’s a small data set taken from three tables of a MySQL database. Element names match table names and the attribute names match columns.

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
	<recipe id="2" name="Bing Cherry Salad"
		description="Jello-like salad with bing cherries" recipe_type_id="2"
		staff_xref_id="1" is_deleted="N" />
	<recipe id="3" name="Chocolate Chip Pound Cake" description="Yummy cake"
		recipe_type_id="2" staff_xref_id="1" is_deleted="Y" />
	<recipe_ext_xref id="1" person_id="4" xref_type="P" />
	<bon_app_ext.person id="2" first_name="Bill" last_name="Siggelkow" />
</dataset>

For a small database where you only need to load a few tables with a few rows, you can hand code the data set. But you wouldn’t want to do that with a complex enterprise databases containing hundreds of tables with cryptic column names. It’s easier if you let DBUnit extract the data for you.

Here’s the typical process you follow when using DBUnit:

  1. Populate your database tables as needed for the test.
  2. Extract data from the those tables into an XML data set.
  3. In your unit test’s setUp method, call DBUnit to load the database from the data set.

You can extract the data using a class like this:

import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;

import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;

public class ExtractTestDataSet {
    public static void main(String[] args) throws Exception {
        // database connection
        Class driverClass = Class.forName("com.mysql.jdbc.Driver");
        Connection jdbcConnection = DriverManager.getConnection(
                "jdbc:mysql://localhost/bon_app", "scott", "tiger");
        IDatabaseConnection connection = new DatabaseConnection(jdbcConnection);

        // partial database export
        QueryDataSet partialDataSet = new QueryDataSet(connection);
        partialDataSet.addTable("recipe", "SELECT * FROM recipe where is_deleted != 'Y'");
        partialDataSet.addTable("recipe_ext_xref");
        FlatXmlDataSet.write(partialDataSet,
                new FileOutputStream("partial-dataset.xml"));

        // full database export
        IDataSet fullDataSet = connection.createDataSet();
        FlatXmlDataSet.write(fullDataSet, new FileOutputStream("full-dataset.xml"));
    }
}

DBUnit can create a data set containing all the data in your database given only an open JDBC connection. In practice, you’ll probably want to limit the dataset to the specific tables and data used by your unit test. In the ExtractTestDataSetexample above, I am actually creating two dataset files. The first one, partial-dataset.xml, represents only a portion of the database. DBUnit populates the dataset based on specified tables and queries. If you give just a table name, the dataset will contain all rows and columns in that table. Specify a table and query, and DBUnit will only extract data returned by the query. If you want the entire database in the data set, simply call the IDatabaseConnection.createDataSet() method.

In your unit test’s setUp method(), you use the data set as input for a DBUnit DatabaseOperation. DBUnit supports various combinations of operations for deleting, inserting, and updating the database using the data set. The most common operations are CLEAN_INSERT and REFRESH. CLEAN_INSERT deletes all rows from tables in the data set, then inserts the data in the data set. This operation is the option to use if you have your own database instance and you don’t need to worry about stepping on someone else’s shoes.
If your DBA is not so gracious, you’re probably sharing a database. In this case, REFRESH is your friend. It only performs updates and inserts based on the data set.

In the following unit test snippet, I use CLEAN_INSERT to re-initialize the database before each test method.

    protected void setUp() throws Exception {
        setUpDatabase();
    }

    private void setUpDatabase() throws Exception {
        // initialize your database connection here
        Class driverClass = Class.forName("com.mysql.jdbc.Driver");
        Connection jdbcConnection = DriverManager.getConnection(
                "jdbc:mysql://localhost/bon_app", "scott", "tiger");
        IDatabaseConnection connection = new DatabaseConnection(jdbcConnection);

        // initialize your dataset here
        IDataSet dataSet = new FlatXmlDataSet(new File("full-dataset.xml"));

        try {
            DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
        } finally {
            connection.close();
        }
    }

This approach worked great for me, but I was annoyed that I had to hand code and recompile the extraction class whenever I needed to change how DBUnit creates the dataset. Since I am using Spring, and I already have this connection configured in my Spring configuration file. If I could leverage Spring to provide a configurable class for extracting data it would save a lot of hand-coding. The result of scratching this itch is the following class:

package com.jadecove.bonappetit.test;

import java.io.FileOutputStream;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;

/**
 * Extracts a DBUnit flat XML dataset from a database.
 *
 * @author Bill Siggelkow
 */
public class DBUnitDataExtractor {

    private DataSource dataSource;
    private String dataSetName = "dbunit-dataset.xml";
    private List queryList;
    private List tableList;
    private Map dbUnitProperties;
    private Map dbUnitFeatures;
    private String schema;

    /**
     * A regular expression that is used to get the table name
     * from a SQL 'select' statement.
     * This  pattern matches a string that starts with any characters,
     * followed by the case-insensitive word 'from',
     * followed by a table name of the form 'foo' or 'schema.foo',
     * followed by any number of remaining characters.
     */
    private static final Pattern TABLE_MATCH_PATTERN = Pattern.compile(".*\s+from\s+(\w+(\.\w+)?).*",
            Pattern.CASE_INSENSITIVE);
    private static final Logger log = Logger.getLogger(DBUnitDataExtractor.class);

    /**
     * The data source of the database from which the data will be extracted. This property
     * is required.
     *
     * @param ds
     */
    public void setDataSource(DataSource ds) {
        dataSource = ds;
    }

    /**
     * Set the schema.
     * @param schema
     */
    public void setSchema(String schema) {
        this.schema = schema;
    }

    /**
     * Name of the XML file that will be created. Defaults to <code>dbunit-dataset.xml</code>.
     *
     * @param name file name.
     */
    public void setDataSetName(String name) {
        dataSetName = name;
    }

    /**
     * Performs the extraction. If no tables or queries are specified, data from entire database
     * will be extracted. Otherwise, a partial extraction will be performed.
     * @throws Exception
     */
    public void extract() throws Exception {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            log.info("Beginning extraction from '"+conn.toString()+"'.");
            IDatabaseConnection connection = new DatabaseConnection(conn, schema);
            configConnection((DatabaseConnection) connection);
            if (tableList != null || queryList != null) {
                // partial database export
                QueryDataSet partialDataSet = new QueryDataSet(connection);
                addTables(partialDataSet);
                addQueries(partialDataSet);
                FlatXmlDataSet.write(partialDataSet,
                        new FileOutputStream(dataSetName));
            } else {
                // full database export
                IDataSet fullDataSet = connection.createDataSet();
                FlatXmlDataSet.write(fullDataSet, new FileOutputStream(dataSetName));
            }
        }
        finally {
            if (conn != null) conn.close();
        }
        log.info("Completed extraction to '"+dataSetName+"'.");
    }

    /**
     * List of table names to extract data from.
     *
     * @param list of table names.
     */
    public void setTableList(List list) {
        tableList = list;
    }

    /**
     * List of SQL queries (i.e. 'select' statements) that will be used executed to retrieve
     * the data to be extracted. If the table being queried is also specified in the <code>tableList</code>
     * property, the query will be ignored and all rows will be extracted from that table.
     *
     * @param list of SQL queries.
     */
    public void setQueryList(List list) {
        queryList = list;
    }

    public void setDbUnitFeatures(Map dbUnitFeatures) {
        this.dbUnitFeatures = dbUnitFeatures;
    }

    public void setDbUnitProperties(Map dbUnitProperties) {
        this.dbUnitProperties = dbUnitProperties;
    }

    private void configConnection(DatabaseConnection conn) {
        DatabaseConfig config = conn.getConfig();
        if (dbUnitProperties != null) {
            for (Iterator k=dbUnitProperties.entrySet().iterator(); k.hasNext(); ) {
                Map.Entry entry = (Map.Entry) k.next();
                String name = (String) entry.getKey();
                Object value = entry.getValue();
                config.setProperty(name, value);
            }
        }
        if (dbUnitFeatures != null) {
            for (Iterator k=dbUnitFeatures.entrySet().iterator(); k.hasNext(); ) {
                Map.Entry entry = (Map.Entry) k.next();
                String name = (String) entry.getKey();
                boolean value = Boolean.valueOf((String)entry.getValue()).booleanValue();
                config.setFeature(name, value);
            }
        }
    }

    private void addTables(QueryDataSet dataSet) {
        if (tableList == null) return;
        for (Iterator k = tableList.iterator(); k.hasNext(); ) {
            String table = (String) k.next();
            dataSet.addTable(table);
        }
    }

    private void addQueries(QueryDataSet dataSet) {
        if (queryList == null) return;
        for (Iterator k = queryList.iterator(); k.hasNext(); ) {
            String query = (String) k.next();
            Matcher m = TABLE_MATCH_PATTERN.matcher(query);
            if (!m.matches()) {
                log.warn("Unable to parse query. Ignoring '" + query +"'.");
            }
            else {
                String table = m.group(1);
                // only add if the table has not been added
                if (tableList != null && tableList.contains(table)) {
                    log.warn("Table '"+table+"' already added. Ignoring '" + query + "'.");
                } else {
                    dataSet.addTable(table, query);
                }
            }
        }
    }
}

This class serves as a Spring-friendly wrapper around the DBUnit extraction process, though it has no dependency on Spring (the beauty of Spring). Here’s how you can configure the extractor in a Spring beans file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>
        <property name="url"><value>jdbc:mysql://localhost/bon_app</value></property>
        <property name="username"><value>scott</value></property>
        <property name="password"><value>tiger</value></property>
    </bean>

	<bean id="dbUnitDataExtractor" class="com.jadecove.bonappetit.test.DBUnitDataExtractor">
		<property name="dataSource" ref="dataSource"/>
		<property name="dataSetName" value="dbunit-dataset.xml"/>
		<property name="tableList">
			<list>
				<value>recipe</value>
				<value>recipe_ext_xref</value>
			</list>
		</property>
		<property name="queryList">
			<list>
				<value>select * from bon_app_ext.person where first_name = 'Bill'</value>
			</list>
		</property>
	</bean>
</beans>

You can now extract the DBUnit dataset by acquiring a configured extractor via Spring, and calling the extract() method. The only required extractor property is the data source. Doing so will create a data set for the entire database. Optionally, you can list specific tables and queries to base the extraction on. In the example above, I am selecting all rows from the recipe and recipe_ext_xref tables. In addition, I am selecting only those rows from the person table, in the bon_app_ext schema, where the first_name equals ‘Bill’.

When using queries, DBUnit requires you to specify both the table name and the query. To make configuration easier, my extractor uses a regular expression to determine the table name. If you happen to specify a query for a table that’s also specifed in the tableList property, the extractor will ignore the query and fetch all rows for the given table.

The extractor also supports additional DBUnit configuration properties and features. DBUnit documents these settings in the DBUnit API and user documents.

I have used this extractor with simple local MySQL databases and with complex legacy enterprise databases. Through a combination of the connection settings, schema, and DBUnit features you can extract the data from most any database. In my current consulting job, I used this extractor to fetch data from some complex DB2 databases running on an IBM AS-400.

This exercise not only helped make life with DBUnit easier, it helped me understand how easy it is to leverage Spring for these mundane assembly tasks. The next time you need to automate tasks like these and you are Spring, don’t forget that it’s well-suited for jobs like this.

What’s been your experience with DBUnit? Or perhaps you have used Spring in a similar fashion? Let’s talk about it.

Tim O

AddThis Social Bookmark Button

Related link: http://www.mouser.com/catalog/623/1452.pdf

First it was a single cell phone charger a few months ago. I marked it up to the joys of feline parenthood. Thirty dollars later, I had a replacement charger from Sprint, but my cat decided to chew through that one the same day I brought it home. Then it got worse…

Anyone else have a cat that enjoys eating wires.