I need to do a bit of integration testing with Grails and both of the DBUnit plugins I found didn’t seem to be maintained. To create a simple base class for DBUnit based integration tests turned out to be relatively simple to create.
For the purposes of this sample I created a grails project called bookstore with a single domain class called Book in the bookstore package. This is what book domain class looks like:
package bookstore
class Book { String title String author }
First I had to add the DBUnit dependency to /grails-app/conf/BuildConfig.groovy and also enable the maven central repository. This is what the grails.project.dependency.resolution section now looks like:
Next I created a base class for my DBUnit based tests. I created a class named DbunitGroovyTestCase in /test/integration/bookstore. The DataSource property named dataSource is injected from the Spring container when the integration tests are run, and I used the DatabaseDataSourceConnection class to create the DBUnit connection.
While (still) looking around for a Scala SQL equivalent of iBatis, I found this post on stackoverflow. Daniel’s answer let me to Querulous. Several years ago I used the Spring Framework JdbcTemplate and it worked fine, but it was kind of clunky. That experience with JdbcTemplate led directly to using iBatis on that very same project. I have been mostly working with Hibernate/JPA since then, but for a certain class of problems, a SQL centric solution seems a better fit that a full blown ORM.
After looking at Querulous, and seeing how it manages to de-clutter the anonymous inner class clunkyness that is JdbcTemplate I’m left wondering if this isn’t the way I should go.
Here is a quick example from the JdbcTemplate documentation in Java:
Collection actors = this.jdbcTemplate.query( "select first_name, surname from t_actor", new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); actor.setSurname(rs.getString("surname")); return actor; } });
Here is the equivalent with Querulous in Scala:
val actors = queryEvaluator.select("select first_name, surname from t_actor") { row => new Actor(row.getString("first_name"), row.getString("surname")) }
Yesterday I was looking around on the inter-webs for a Scala project that was “iBatis” like, but more Scala oriented. I found some cool stuff (ScalaQL, and scala-sql-dsl for example) but nothing quite what I was looking for.
I kind of chewed on what exactly it was that I was looking for and this is my 10k/foot feature list:
Ability to define the underlying data store in a DSL that can be understood by Scala
Ability to map Scala classes to JDBC results by binding to artifacts of the data store
This is more or less what iBatis accomplishes with xml (now annotations) and a bunch of reflection. I’d like to see it done without XML and minimal reflection.
That sent me down the rabbit hole on how exactly one goes about defining a DSL in scala. I re-read chapter 31 in Programming in Scala. I also read chapter 11 in Programming Scala. Then I took a stab at writing a parser combinator for a very, very small subset of the “CREATE TABLE” DDL syntax for SQL.
def main(args: Array[String]) { val input = """TABLE person (first_name VARCHAR, last_name VARCHAR, age INTEGER) TABLE place (city VARCHAR, state VARCHAR)""" println(parseAll(tables,input)) }
}
Here is the output
[2.51] parsed: Map(person -> Map(first_name -> VARCHAR, last_name -> VARCHAR, age -> INTEGER), place -> Map(city -> VARCHAR, state -> VARCHAR))
This experiment only supports two data types (VARCHAR and INTEGER), without any of the other metadata that is required for those types. I just wanted a feel for how hard it would be to write and didn’t want to get bogged down in the details. It turned out to be quite a bit easier than I thought it would be.
Next up is to figure out the best way to map this DDL information to a scala class (If I don’t get distracted by something else first).
A quick tip so you don’t have to re-enter your user name and password every time you pull or push from a remote Mercurial repository when using TortoiseHg.
Right click in the folder explorer and select TortoiseHg –> Repository Settings.
Select the “Sync” tab in the “TortoiseHg Configure Repository” window. Either select the desired Remote repository path and click “Edit” or create a new Remote repository path with the “Add” button.
That will bring up the “Edit remote repository path” dialog. Enter your User and Password, then click OK.
Finally click the “Apply” button on the “TortoiseHg Configure Repository” window.
In the "code club" / "dojo" or whatever you want to call it that Ben (aka Johnny Coder) organized we have been learning about Scala the last few weeks. I have been puttering with Scala on my own for about a year now, and it has been fun to engage with other folk in the learning process. We have mostly been doing a mix of Project Euler and the S-99 problems as exercises. Those problems are fun, but they don't really reflect the kind of day to day programming tasks that I am usually engaged in.
To scratch that slightly more practical itch, I decided to see how well Scala fits in with other tools in the Java ecosystem. The Java ecosystem being broad, and my Scala kung fu being not that strong, all I have are some possibly interesting bits I discovered while trying to get Scala and RESTEasy working together. I picked RESTEasy because in the middle of reading RESTful Java with JAX-RS.
I started off with an existing example project from chapter 11 in the RESTEasy book. I added the Scala plugin to the maven build, and I was off.
I ran into a few things that caused me problems. The first was this compiler message:
"';' expected but ',' found"
At the @XMLType annotation line. The resolution was to switch from using the Array literal "{}" Java syntax to the Scala Array object.
I also got a JAXB runtime error during the JUnit tests:
.... Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 15 counts of IllegalAnnotationExceptions There are two properties named ....
I resolved that by using the @XmlAccessorType(XmlAccessType.FIELD) annotation.
For compatibility with the existing code that expected Java Bean style property access, I added the @BeanProperty annotation. The annotation causes Scala to automatically generate get/set methods.
This conversion was very similar, but it was dealing with collections. I found that Scala 2.8 has a new object scala.collection.JavaConversions that contains many implicit conversion for Java collections.
/** * @author Bill Burke * @version $Revision: 1 $ */ @XmlRootElement(name = "customers") public class Customers { protected Collection customers = new ArrayList(); protected List links;
@XmlElementRef public Collection getCustomers() { return customers; }
public void setCustomers(Collection customers) { this.customers = customers; }
@XmlElementRef public List getLinks() { return links; }
public void setLinks(List links) { this.links = links; }
@XmlTransient public String getNext() { if (links == null) return null; for (Link link : links) { if ("next".equals(link.getRelationship())) return link.getHref(); } return null; }
@XmlTransient public String getPrevious() { if (links == null) return null; for (Link link : links) { if ("previous".equals(link.getRelationship())) return link.getHref(); } return null; }
private def findHref(rname: String): String = { val rel = links.filter(_.getRelationship == rname) rel match { case Buffer() => null case _ => rel.head.getHref } }
}
That’s about as far as I got this weekend. I guess I was actually mostly working with JAXB this time. The same example code has JPA as was as JAX-RS, and I’m going to try and convert the entire project to Scala, and if I do, I’ll upload it to BitBucket.
Next I created the ivy.xml file. After playing around with a couple of different configurations, I settled on this one. Any future libraries added to the project would be either assigned to "compile" or "runtime" configurations. The "compile" runtime would include things like 3rd party GWT libraries. The "runtime" configuration would be used for dependencies executed in the Servlet container.
<target name="download-ivy" unless="offline"> <mkdir dir="${ivy.jar.dir}"/> <!-- download Ivy from web site so that it can be used even without any special installation --> <get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" dest="${ivy.jar.file}" usetimestamp="true"/> </target>
<target name="init-ivy" depends="download-ivy"> <!-- try to load ivy here from ivy home, in case the user has not already dropped it into ant's lib dir (note that the latter copy will always take precedence). We will not fail as long as local lib dir exists (it may be empty) and ivy is in at least one of ant's lib dir or the local lib dir. --> <path id="ivy.lib.path"> <fileset dir="${ivy.jar.dir}" includes="*.jar"/> </path> <taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/> </target>
<target name="download-ivy" unless="offline"> <mkdir dir="${ivy.jar.dir}"/> <!-- download Ivy from web site so that it can be used even without any special installation --> <get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" dest="${ivy.jar.file}" usetimestamp="true"/> </target>
<target name="init-ivy" depends="download-ivy"> <!-- try to load ivy here from ivy home, in case the user has not already dropped it into ant's lib dir (note that the latter copy will always take precedence). We will not fail as long as local lib dir exists (it may be empty) and ivy is in at least one of ant's lib dir or the local lib dir. --> <path id="ivy.lib.path"> <fileset dir="${ivy.jar.dir}" includes="*.jar"/> </path> <taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/> </target>
<target name="gwtc" depends="javac" description="GWT compile to JavaScript"> <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler"> <classpath> <pathelement location="src"/> <path refid="project.class.path"/> </classpath> <!-- add jvmarg -Xss16M or similar if you see a StackOverflowError --> <jvmarg value="-Xmx256M"/> <!-- Additional arguments like -style PRETTY or -logLevel DEBUG --> <arg value="com.mostlyblather.IvyGwt"/> </java> </target>