Java

Using Spring for unit testing Hibernate DAO

Most non-trivial Hibernate applications require us to keep a Hibernate Session around. In our webapp we us the HibernateInterceptor to manage the session. This doesn’t help us when we want to unit test libraries that use Hibernate.

A convenient solution to this issue is having the tests extend org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests (there are corresponding classes for other testing frameworks as well). Gotta love those Spring class names :-)

AbstractTransactionalJUnit4SpringContextTests helps us by automatically binding a session to the thread. Each test is wrapped in a transaction that owns the session. When the test completes the transaction is rolled back and the session destroyed.

Example:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:mapDbContext.xml"})
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
public class PoiDaoTest extends AbstractTransactionalJUnit4SpringContextTests
{
    @Autowired
    private PoiDao poiDao;
 
    @Autowired
    private DataSource dataSource;
 
    @Autowired
    private SessionFactory sessionFactory;
    private Poi thePoi = new Poi("test", "poi1", null, 42, 17, 1, 1, 60.34, 17.23);
 
    @Test
    public void update() throws Exception {
       thePoi.setDescription("Test Description");
        poiDao.update(thePoi);
        assertEquals("Test Description", poiDao.load(thePoi.getId()).getDescription());
    }
}

Running Google Appengine Java apps in Intellij Idea

After having created a standard maven project for the guestbook app as described in this previous post, running the application in Intellij Idea is pretty straight forward.

Create the project

Select File > New Project, and create a new project from External Model, Maven by pointing idea to your pom.xml, just as you would to import any other maven project.

Create a Runner

While we are waiting for someone to write a Intellij Idea server component for Google Appengine Java SDK, we have to create a runner for the server, with the following parameters:

Main Class

com.google.appengine.tools.KickStart

VM Parameters

We have to pass this parameter for Appengine to accept loading the tools jar from a non-standard location.

-Dappengine.sdk.root="/Users/<your user>/.m2/repository/com/google/appengine-tools/1.2.0/"

Program Parameters

com.google.appengine.tools.development.DevAppServerMain  target/guestbook

Working Directory

/Users/<your user>/dev/google-app-engine/guestbook

Environment Variables

 
SDK_LIB=/Users/<your user>/dev/google-app-engine/appengine-java-sdk/lib;
 
SDK_CONFIG=/Users/<your user>/dev/google-app-engine/appengine-java-sdk/config/sdk

Run the App

You should se the following in the runner console tab:

/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java -Dappengine.sdk.root=/Users/torstenek/.m2/repository/com/google/appengine-tools/1.2.0/ -Dfile.encoding=MacRoman -classpath /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/deploy.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/dt.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/javaws.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/management-agent.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/plugin.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/sa-jdi.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/charsets.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/dt.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/jconsole.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/jsse.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/laf.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/management-agent.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/../Classes/ui.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/apple_provider.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/dnsns.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/localedata.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/sunjce_provider.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/sunpkcs11.jar:/Users/torstenek/dev/google-app-engine/guestbook/target/classes:/Users/torstenek/dev/google-app-engine/appengine-java-sdk/lib/appengine-tools-api.jar:/Users/torstenek/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar:/Users/torstenek/.m2/repository/com/google/appengine-tools/1.2.0/appengine-tools-1.2.0.jar:/Users/torstenek/.m2/repository/com/google/appengine-local-runtime-shared/1.2.0/appengine-local-runtime-shared-1.2.0.jar:/Users/torstenek/.m2/repository/com/google/appengine-sdk-1.2.0-api/1.2.0/appengine-sdk-1.2.0-api-1.2.0.jar:/Users/torstenek/.m2/repository/taglibs/standard/1.1.2/standard-1.1.2.jar:/Users/torstenek/.m2/repository/javax/servlet/jstl/1.1.2/jstl-1.1.2.jar:/Users/torstenek/.m2/repository/org/apache/geronimo/specs/geronimo-el_1.0_spec/1.0.1/geronimo-el_1.0_spec-1.0.1.jar:/Users/torstenek/.m2/repository/org/apache/geronimo/specs/geronimo-jsp_2.1_spec/1.0.1/geronimo-jsp_2.1_spec-1.0.1.jar:/Users/torstenek/.m2/repository/org/apache/geronimo/specs/geronimo-servlet_2.5_spec/1.2/geronimo-servlet_2.5_spec-1.2.jar:/Users/torstenek/.m2/repository/org/apache/geronimo/specs/geronimo-jpa_3.0_spec/1.1.1/geronimo-jpa_3.0_spec-1.1.1.jar:/Users/torstenek/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/Users/torstenek/.m2/repository/org/datanucleus/datanucleus-appengine/1.0.0.final/datanucleus-appengine-1.0.0.final.jar:/Users/torstenek/.m2/repository/javax/jdo/jdo2-api/2.3-SNAPSHOT/jdo2-api-2.3-SNAPSHOT.jar:/Users/torstenek/.m2/repository/org/datanucleus/datanucleus-core/1.1.1/datanucleus-core-1.1.1.jar:/Users/torstenek/.m2/repository/javax/transaction/transaction-api/1.1/transaction-api-1.1.jar:/Users/torstenek/.m2/repository/org/datanucleus/datanucleus-jpa/1.1.1/datanucleus-jpa-1.1.1.jar:/Users/torstenek/.m2/repository/javax/persistence/persistence-api/1.0/persistence-api-1.0.jar com.google.appengine.tools.KickStart com.google.appengine.tools.development.DevAppServerMain target/erpilot2
2009-04-11 12:44:09.504 java[38706:10b] [Java CocoaComponent compatibility mode]: Enabled
2009-04-11 12:44:09.505 java[38706:10b] [Java CocoaComponent compatibility mode]: Setting timeout for SWT to 0.100000
The server is running at http://localhost:8080/

Now you can point your browser to:
http://localhost:8080

Maven project for Google Appengine Java SDK

Google Appengine ships with an Eclipse plugin which appears to assume a particular project structure. Since some of us don’t use Eclipse, and all of us use maven, we prefer to use the standard maven structure for the project, which works equally well standalone, and with all major IDEs.

Because the jars distributed Google App Engine appear to different from the same version jars available in the maven repositories, this procedure will require you to add pretty much all Google distributed jars to your local repository which is obviously far from ideal.

Download the SDK

Start by downloading the Google Appengine SDK for Java here: http://code.google.com/appengine/downloads.html, and unzip the distribution package, in our case appengine-java-sdk-1.2.0.zip

Publish the library jars to Maven repository

Next publish the appengine libraries to your maven repository so that we can list them as dependencies in your pom.xml file.

mvn install:install-file -Dfile=lib/appengine-tools-api.jar -DgroupId=com.google -DartifactId=appengine-tools -Dversion=1.2.0 -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/user/appengine-api-1.0-sdk-1.2.0.jar -DgroupId=com.google -DartifactId=appengine-sdk-1.2.0-api -Dversion=1.2.0 -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/shared/appengine-local-runtime-shared.jar -DgroupId=com.google -DartifactId=appengine-local-runtime-shared -Dversion=1.2.0 -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/user/orm/datanucleus-appengine-1.0.0.final.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-appengine -Dversion=1.0.0.final -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/user/orm/datanucleus-appengine-1.0.0.final.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-appengine -Dversion=1.0.0.final -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/user/orm/datanucleus-core-1.1.0.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-core -Dversion=1.1.0 -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -Dfile=lib/user/orm/datanucleus-jpa-1.1.0.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-jpa -Dversion=1.1.0 -DgeneratePom=true -Dpackaging=jar

We are also going to need the jdo-api-2.3-SNAPSHOT artifacts and the transactiona-api-1.1.jar (which can be downloaded from http://download.java.net/maven/1/javax.transaction/jars/ ) as they do not appear to be available in the central maven repository.

mvn install:install-file -Dfile=lib/user/orm/jdo2-api-2.3-SNAPSHOT.jar -DgroupId=javax.jdo -DartifactId=jdo2-api -Dversion=2.3-SNAPSHOT -DgeneratePom=true -Dpackaging=jar
 
mvn install:install-file -DgroupId=javax.transaction -DartifactId=transaction-api           -Dversion=1.1 -Dpackaging=jar -Dfile=/Users/torstenek/Desktop/transaction-api-1.1.jar

The enhancer tools from central has a dependency to a different version of datanucleus-core, so we need to create a local artifact for this one as well.

mvn install:install-file -Dfile=lib/tools/orm/datanucleus-enhancer-1.1.0.jar -DgroupId=org.datanucleus -DartifactId=datanucleus-enhancer -Dversion=1.1.0 -DgeneratePom=true -Dpackaging=jar

This project also uses the maven-datanucleus-plugin (see the POM). We had to hack the pom file to ensure that the datanucleus dependencies match point to the versions we just added manually to the repository.

repository/org/datanucleus/maven-datanucleus-plugin/1.1.0/

Create the Maven project structure and pom.xml

The simplest way to create a maven webapp project is to use the maven archetype plugin. Let’s create a maven version of the guestbook demo included in the SDK.

mvn archetype:create -DgroupId=com.google -DartifactId=guestbook -DarchetypeArtifactId=maven-archetype-webapp

Configure your POM

The finished POM for the guestbook project should look something like this

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.google</groupId>
    <artifactId>guestbook</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>guestbook Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google</groupId>
            <artifactId>appengine-tools</artifactId>
            <version>1.2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google</groupId>
            <artifactId>appengine-local-runtime-shared</artifactId>
            <version>1.2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google</groupId>
            <artifactId>appengine-sdk-1.2.0-api</artifactId>
            <version>1.2.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <artifactId>standard</artifactId>
            <groupId>taglibs</groupId>
            <version>1.1.2</version>
            <type>jar</type>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <artifactId>jstl</artifactId>
            <groupId>javax.servlet</groupId>
            <version>1.1.2</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-el_1.0_spec</artifactId>
            <version>1.0.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jsp_2.1_spec</artifactId>
            <version>1.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-servlet_2.5_spec</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jpa_3.0_spec</artifactId>
            <version>1.1.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-jta_1.1_spec</artifactId>
            <version>1.1.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-appengine</artifactId>
            <version>1.0.0.final</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>javax.jdo</groupId>
            <artifactId>jdo2-api</artifactId>
            <version>2.3-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-core</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.datanucleus</groupId>
            <artifactId>datanucleus-jpa</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>DataNucleus_Repos2</id>
            <name>DataNucleus Repository</name>
            <url>http://www.datanucleus.org/downloads/maven2</url>
        </repository>
    </repositories>
    <build>
        <finalName>guestbook</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.datanucleus</groupId>
                <artifactId>maven-datanucleus-plugin</artifactId>
                <version>1.1.0</version>
                <configuration>
                    <mappingIncludes>**/*.class</mappingIncludes>
                    <verbose>true</verbose>
                    <enhancerName>ASM</enhancerName>
                    <api>JPA</api>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>enhance</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

After you execute %mvn package your deployable app will be in the folder target/guestbook. You should be able to deploy it and run it using the standard instructions for the Appengine Java SDK. I’m moving on to make sure I can run it in Intellij Idea. More on that later.

I’m still waiting for my Java account to activate at Google so I haven’t tried uploading the app yet.

xmlbeans and Maven – essential tools in a RESTful world

A day rarely goes by without the need arising to parse or consume some custom XML. In some cases we get an example of the data, more rarely a proper XML schema. In the former case the best thing to do is usually to reverse engineer the schema.

We drop this schema into the someproject/src/main/resources/schemas folder, compile our project using maven, and presto – we have a nice Java API to any XML conforming to the schema.

To accomplish this we add the xmlbeans as adependency to our pom.xml

<dependency>
   <groupId>org.apache.xmlbeans</groupId>
   <artifactId>xmlbeans</artifactId>
   <version>2.3.0</version>
</dependency>

We also add the xmlbeans plugin to our pom.xml like so:

<plugins>
   <plugin>
     <groupId>org.codehaus.mojo</groupId>
     <artifactId>xmlbeans-maven-plugin</artifactId>
     <executions>
          <execution>
          <goals>
              <goal>xmlbeans</goal>
          </goals>
          </execution>
     </executions>
     <inherited>true</inherited>
     <configuration>
         <schemaDirectory>src/main/resources/schemas</schemaDirectory>
         <sourceGenerationDirectory>src/main/java</sourceGenerationDirectory>
         <verbose>true</verbose>
         <noJavac>true</noJavac>
     </configuration>
   </plugin>
             ....
</plugins>

We can now run the xmlbeans goal manually to generate the initial sources:

% mvn xmlbeans:xmlbeans

Whenever we run mvn compile (or any other goal that depends on generating sources) the schema folder will be examined and if changes are detected to a schema its xmlbeans types will be re-generated.